mirror of
https://github.com/revanced/revanced-integrations.git
synced 2025-01-20 16:57:32 +01:00
chore: Merge branch dev
to main
(#511)
This commit is contained in:
commit
d0c659ce11
69
CHANGELOG.md
69
CHANGELOG.md
@ -1,3 +1,72 @@
|
||||
# [0.122.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.122.0-dev.3...v0.122.0-dev.4) (2023-11-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide layout components:** Hide description components ([726a251](https://github.com/ReVanced/revanced-integrations/commit/726a2510a5b28da3afb77c7f5fdda87a3bd2c809))
|
||||
|
||||
# [0.122.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.122.0-dev.2...v0.122.0-dev.3) (2023-11-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Support version `18.43.45` and `18.44.41` ([#514](https://github.com/ReVanced/revanced-integrations/issues/514)) ([a5245b8](https://github.com/ReVanced/revanced-integrations/commit/a5245b85a829a86b535cf305cac49d14b709708d))
|
||||
|
||||
# [0.122.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.122.0-dev.1...v0.122.0-dev.2) (2023-11-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Disable resuming Shorts on startup:** Adjust patch name ([#516](https://github.com/ReVanced/revanced-integrations/issues/516)) ([8b5d2d1](https://github.com/ReVanced/revanced-integrations/commit/8b5d2d1871c19421eb39ce38a7039c86e5d8d08b))
|
||||
|
||||
# [0.122.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.121.1-dev.4...v0.122.0-dev.1) (2023-11-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Disable suggested video end screen:** Properly hide it every time the screen appears ([828ff6f](https://github.com/ReVanced/revanced-integrations/commit/828ff6f31e2f15bf50899ab1e403bdc40cc09d07))
|
||||
* **YouTube - Hide layout components:** Reduce false positives when hiding mix playlists ([5f30100](https://github.com/ReVanced/revanced-integrations/commit/5f30100fd59c1e61c0236bc54cfcd03212994cab))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Add `Enable slide to seek` patch ([b1ce7a7](https://github.com/ReVanced/revanced-integrations/commit/b1ce7a75eba53312d9522c87321ac83cb16d83cf))
|
||||
* **YouTube:** Add `Remove tracking query parameter` patch ([e84b7b3](https://github.com/ReVanced/revanced-integrations/commit/e84b7b328ea48e86d240d38cf83aa960f87d6902))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **YouTube - Client spoof:** Reduce timeout to fetch storyboard renderer ([847cce4](https://github.com/ReVanced/revanced-integrations/commit/847cce43f6436c592c680820960f5270f799cb8d))
|
||||
|
||||
## [0.121.1-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.121.1-dev.3...v0.121.1-dev.4) (2023-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Client spoof:** Fix low resolution precise seeking thumbnails ([#513](https://github.com/ReVanced/revanced-integrations/issues/513)) ([11f97ac](https://github.com/ReVanced/revanced-integrations/commit/11f97ac354344aac3d101f8874e38273da15f9e6))
|
||||
|
||||
## [0.121.1-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.121.1-dev.2...v0.121.1-dev.3) (2023-11-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Remove screenshot restriction:** Improve reliability ([#471](https://github.com/ReVanced/revanced-integrations/issues/471)) ([50933dc](https://github.com/ReVanced/revanced-integrations/commit/50933dc42d4ef77fb45a978e19d306dad5070540))
|
||||
|
||||
## [0.121.1-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.121.1-dev.1...v0.121.1-dev.2) (2023-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Disable suggested video end screen:** Do not spam click to disable the screen ([4f57d56](https://github.com/ReVanced/revanced-integrations/commit/4f57d560425a40386a014da05fe26bb9c22f090f))
|
||||
* **YouTube:** Prevent playing touch interaction sound when unintended ([6e414ec](https://github.com/ReVanced/revanced-integrations/commit/6e414ec6c2a40d70f810b0ade1d8c41cadafff8d))
|
||||
|
||||
## [0.121.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.121.0...v0.121.1-dev.1) (2023-11-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Rename `Restore old seekbar thumbnails` and `Restore old quality menu` ([#510](https://github.com/ReVanced/revanced-integrations/issues/510)) ([6b00f90](https://github.com/ReVanced/revanced-integrations/commit/6b00f90fb7561d59de59d76d43fff8c1d057dce6))
|
||||
|
||||
# [0.121.0](https://github.com/ReVanced/revanced-integrations/compare/v0.120.0...v0.121.0) (2023-11-04)
|
||||
|
||||
|
||||
|
@ -5,6 +5,10 @@ import android.view.WindowManager;
|
||||
|
||||
public class RemoveScreenshotRestrictionPatch {
|
||||
|
||||
public static void addFlags(Window window, int flags) {
|
||||
window.addFlags(flags & ~WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
public static void setFlags(Window window, int flags, int mask) {
|
||||
window.setFlags(flags & ~WindowManager.LayoutParams.FLAG_SECURE, mask & ~WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
/** @noinspection unused*/
|
||||
public class DisableResumingStartupShortsPlayerPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean disableResumingStartupShortsPlayer() {
|
||||
return SettingsEnum.DISABLE_RESUMING_SHORTS_PLAYER.getBoolean();
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public class DisableStartupShortsPlayerPatch {
|
||||
//Used by app.revanced.patches.youtube.layout.startupshortsreset.patch.DisableShortsOnStartupPatch
|
||||
public static boolean disableStartupShortsPlayer() {
|
||||
return SettingsEnum.DISABLE_RESUMING_SHORTS_PLAYER.getBoolean();
|
||||
}
|
||||
}
|
@ -14,13 +14,17 @@ public final class DisableSuggestedVideoEndScreenPatch {
|
||||
public static void closeEndScreen(final ImageView imageView) {
|
||||
if (!SettingsEnum.DISABLE_SUGGESTED_VIDEO_END_SCREEN.getBoolean()) return;
|
||||
|
||||
// Get the view which can be listened to for layout changes.
|
||||
// Get a parent view which can be listened to for layout changes.
|
||||
final var parent = imageView.getParent().getParent();
|
||||
|
||||
// Prevent adding the listener multiple times.
|
||||
if (lastView == parent) return;
|
||||
|
||||
lastView = (ViewGroup)parent;
|
||||
lastView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> imageView.performClick());
|
||||
lastView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||
// Disable sound effects to prevent the click sound.
|
||||
imageView.setSoundEffectsEnabled(false);
|
||||
imageView.performClick();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public final class EnableOldSeekbarThumbnailsPatch {
|
||||
public static boolean enableOldSeekbarThumbnails() {
|
||||
return !SettingsEnum.ENABLE_OLD_SEEKBAR_THUMBNAILS.getBoolean();
|
||||
}
|
||||
}
|
@ -2,28 +2,27 @@ package app.revanced.integrations.patches;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
public class HideBreakingNewsPatch {
|
||||
|
||||
/**
|
||||
* When spoofing to app versions older than 17.30.35, the watch history preview bar uses
|
||||
* When spoofing to app versions 17.31.00 and older, the watch history preview bar uses
|
||||
* the same layout components as the breaking news shelf.
|
||||
*
|
||||
* Breaking news does not appear to be present in these older versions anyways.
|
||||
*/
|
||||
private static boolean isSpoofingOldVersionWithHorizontalCardListWatchHistory() {
|
||||
return SettingsEnum.SPOOF_APP_VERSION.getBoolean()
|
||||
&& SettingsEnum.SPOOF_APP_VERSION_TARGET.getString().compareTo("17.30.35") < 0;
|
||||
}
|
||||
private static final boolean isSpoofingOldVersionWithHorizontalCardListWatchHistory =
|
||||
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("17.31.00");
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void hideBreakingNews(View view) {
|
||||
if (!SettingsEnum.HIDE_BREAKING_NEWS.getBoolean()
|
||||
|| isSpoofingOldVersionWithHorizontalCardListWatchHistory()) return;
|
||||
|| isSpoofingOldVersionWithHorizontalCardListWatchHistory) return;
|
||||
ReVancedUtils.hideViewByLayoutParams(view);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public final class RemoveTrackingQueryParameterPatch {
|
||||
private static final String NEW_TRACKING_PARAMETER_REGEX = ".si=.+";
|
||||
private static final String OLD_TRACKING_PARAMETER_REGEX = ".feature=.+";
|
||||
|
||||
public static String sanitize(String url) {
|
||||
if (!SettingsEnum.REMOVE_TRACKING_QUERY_PARAMETER.getBoolean()) return url;
|
||||
|
||||
return url
|
||||
.replaceAll(NEW_TRACKING_PARAMETER_REGEX, "")
|
||||
.replaceAll(OLD_TRACKING_PARAMETER_REGEX, "");
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class RestoreOldSeekbarThumbnailsPatch {
|
||||
public static boolean useFullscreenSeekbarThumbnails() {
|
||||
return !SettingsEnum.RESTORE_OLD_SEEKBAR_THUMBNAILS.getBoolean();
|
||||
}
|
||||
}
|
@ -1,19 +1,20 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike.Vote;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.*;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import app.revanced.integrations.patches.components.ReturnYouTubeDislikeFilterPatch;
|
||||
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.shared.PlayerType;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
@ -21,12 +22,7 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import app.revanced.integrations.patches.components.ReturnYouTubeDislikeFilterPatch;
|
||||
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.shared.PlayerType;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike.Vote;
|
||||
|
||||
/**
|
||||
* Handles all interaction of UI patch components.
|
||||
@ -35,13 +31,14 @@ import app.revanced.integrations.utils.ReVancedUtils;
|
||||
* Litho based Shorts player can experience temporarily frozen video playback if the RYD fetch takes too long.
|
||||
*
|
||||
* Temporary work around:
|
||||
* Enable app spoofing to version 18.20.39 or older, as that uses a non litho Shorts player.
|
||||
* Enable app spoofing to version 18.33.40 or older, as that uses a non litho Shorts player.
|
||||
*
|
||||
* Permanent fix (yet to be implemented), either of:
|
||||
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes asynchronously.
|
||||
* - Find a way to force Litho to rebuild it's component tree
|
||||
* (and use that hook to force the shorts dislikes to update after the fetch is completed).
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ReturnYouTubeDislikePatch {
|
||||
|
||||
/**
|
||||
@ -75,12 +72,18 @@ public class ReturnYouTubeDislikePatch {
|
||||
if (!rydEnabled) {
|
||||
// Must remove all values to protect against using stale data
|
||||
// if the user enables RYD while a video is on screen.
|
||||
currentVideoData = null;
|
||||
lastLithoShortsVideoData = null;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
clearData();
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearData() {
|
||||
currentVideoData = null;
|
||||
lastLithoShortsVideoData = null;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
// Rolling number text should not be cleared,
|
||||
// as it's used if incognito Short is opened/closed
|
||||
// while a regular video is on screen.
|
||||
}
|
||||
|
||||
//
|
||||
// 17.x non litho regular video player.
|
||||
@ -137,7 +140,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
if (oldUITextView == null) {
|
||||
return;
|
||||
}
|
||||
oldUIReplacementSpan = videoData.getDislikesSpanForRegularVideo(oldUIOriginalSpan, false);
|
||||
oldUIReplacementSpan = videoData.getDislikesSpanForRegularVideo(oldUIOriginalSpan, false, false);
|
||||
if (!oldUIReplacementSpan.equals(oldUITextView.getText())) {
|
||||
oldUITextView.setText(oldUIReplacementSpan);
|
||||
}
|
||||
@ -188,55 +191,70 @@ public class ReturnYouTubeDislikePatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* For Litho segmented buttons and Litho Shorts player.
|
||||
*/
|
||||
@NonNull
|
||||
public static CharSequence onLithoTextLoaded(@NonNull Object conversionContext,
|
||||
@Nullable AtomicReference<CharSequence> textRef,
|
||||
@NonNull CharSequence original) {
|
||||
return onLithoTextLoaded(conversionContext, textRef, original, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a litho text component is initially created,
|
||||
* and also when a Span is later reused again (such as scrolling off/on screen).
|
||||
*
|
||||
* This method is sometimes called on the main thread, but it usually is called _off_ the main thread.
|
||||
* This method can be called multiple times for the same UI element (including after dislikes was added).
|
||||
*
|
||||
* @param textRef Cache reference to the like/dislike char sequence,
|
||||
* @param textRef Optional cache reference to the like/dislike char sequence,
|
||||
* which may or may not be the same as the original span parameter.
|
||||
* If dislikes are added, the atomic reference must be set to the replacement span.
|
||||
* @param original Original span that was created or reused by Litho.
|
||||
* @return The original span (if nothing should change), or a replacement span that contains dislikes.
|
||||
* @param original Original char sequence was created or reused by Litho.
|
||||
* @param isRollingNumber If the span is for a Rolling Number.
|
||||
* @return The original char sequence (if nothing should change), or a replacement char sequence that contains dislikes.
|
||||
*/
|
||||
@NonNull
|
||||
public static CharSequence onLithoTextLoaded(@NonNull Object conversionContext,
|
||||
@NonNull AtomicReference<CharSequence> textRef,
|
||||
@NonNull CharSequence original) {
|
||||
private static CharSequence onLithoTextLoaded(@NonNull Object conversionContext,
|
||||
@Nullable AtomicReference<CharSequence> textRef,
|
||||
@NonNull CharSequence original,
|
||||
boolean isRollingNumber) {
|
||||
try {
|
||||
if (!SettingsEnum.RYD_ENABLED.getBoolean()) {
|
||||
return original;
|
||||
}
|
||||
|
||||
String conversionContextString = conversionContext.toString();
|
||||
// Remove this log statement after the a/b new litho dislikes is fixed.
|
||||
LogHelper.printDebug(() -> "conversionContext: " + conversionContextString);
|
||||
|
||||
final Spanned replacement;
|
||||
final CharSequence replacement;
|
||||
if (conversionContextString.contains("|segmented_like_dislike_button.eml|")) {
|
||||
// Regular video
|
||||
// Regular video.
|
||||
ReturnYouTubeDislike videoData = currentVideoData;
|
||||
if (videoData == null) {
|
||||
return original; // User enabled RYD while a video was on screen.
|
||||
}
|
||||
replacement = videoData.getDislikesSpanForRegularVideo((Spannable) original, true);
|
||||
// When spoofing between 17.09.xx and 17.30.xx the UI is the old layout but uses litho
|
||||
// and the dislikes is "|dislike_button.eml|"
|
||||
// but spoofing to that range gives a broken UI layout so no point checking for that.
|
||||
} else if (conversionContextString.contains("|shorts_dislike_button.eml|")) {
|
||||
if (!(original instanceof Spanned)) {
|
||||
original = new SpannableString(original);
|
||||
}
|
||||
replacement = videoData.getDislikesSpanForRegularVideo((Spanned) original,
|
||||
true, isRollingNumber);
|
||||
|
||||
// When spoofing between 17.09.xx and 17.30.xx the UI is the old layout
|
||||
// but uses litho and the dislikes is "|dislike_button.eml|".
|
||||
// But spoofing to that range gives a broken UI layout so no point checking for that.
|
||||
} else if (!isRollingNumber && conversionContextString.contains("|shorts_dislike_button.eml|")) {
|
||||
// Litho Shorts player.
|
||||
if (!SettingsEnum.RYD_SHORTS.getBoolean()) {
|
||||
// Must clear the current video here, otherwise if the user opens a regular video
|
||||
// then opens a litho short (while keeping the regular video on screen), then closes the short,
|
||||
// the original video may show the incorrect dislike value.
|
||||
currentVideoData = null;
|
||||
clearData();
|
||||
return original;
|
||||
}
|
||||
ReturnYouTubeDislike videoData = lastLithoShortsVideoData;
|
||||
if (videoData == null) {
|
||||
// The Shorts litho video id filter did not detect the video id.
|
||||
// This is normal if in incognito mode, but otherwise is not normal.
|
||||
// This is normal in incognito mode, but otherwise is abnormal.
|
||||
LogHelper.printDebug(() -> "Cannot modify Shorts litho span, data is null");
|
||||
return original;
|
||||
}
|
||||
@ -250,12 +268,12 @@ public class ReturnYouTubeDislikePatch {
|
||||
}
|
||||
LogHelper.printDebug(() -> "Using current video data for litho span");
|
||||
}
|
||||
replacement = videoData.getDislikeSpanForShort((Spannable) original);
|
||||
replacement = videoData.getDislikeSpanForShort((Spanned) original);
|
||||
} else {
|
||||
return original;
|
||||
}
|
||||
|
||||
textRef.set(replacement);
|
||||
if (textRef != null) textRef.set(replacement);
|
||||
return replacement;
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "onLithoTextLoaded failure", ex);
|
||||
@ -263,6 +281,123 @@ public class ReturnYouTubeDislikePatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
//
|
||||
// Rolling Number
|
||||
//
|
||||
|
||||
/**
|
||||
* Current regular video rolling number text, if rolling number is in use.
|
||||
* This is saved to a field as it's used in every draw() call.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile CharSequence rollingNumberSpan;
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String onRollingNumberLoaded(@NonNull Object conversionContext,
|
||||
@NonNull String original) {
|
||||
try {
|
||||
CharSequence replacement = onLithoTextLoaded(conversionContext, null, original, true);
|
||||
if (!replacement.toString().equals(original)) {
|
||||
rollingNumberSpan = replacement;
|
||||
return replacement.toString();
|
||||
} // Else, the text was not a likes count but instead the view count or something else.
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "onRollingNumberLoaded failure", ex);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Rolling Number text view modifications made by this patch.
|
||||
* Required as it appears text views can be reused for other rolling numbers (view count, upload time, etc).
|
||||
*/
|
||||
private static void removeRollingNumberPatchChanges(TextView view) {
|
||||
if (view.getCompoundDrawablePadding() != 0) {
|
||||
LogHelper.printDebug(() -> "Removing rolling number styling from TextView");
|
||||
view.setCompoundDrawablePadding(0);
|
||||
view.setCompoundDrawables(null, null, null, null);
|
||||
view.setGravity(Gravity.NO_GRAVITY);
|
||||
view.setTextAlignment(View.TEXT_ALIGNMENT_INHERIT);
|
||||
view.setSingleLine(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Rolling Number text view modifications.
|
||||
*/
|
||||
private static void addRollingNumberPatchChanges(TextView view) {
|
||||
if (view.getCompoundDrawablePadding() == 0) {
|
||||
LogHelper.printDebug(() -> "Adding rolling number styling to TextView");
|
||||
// YouTube Rolling Numbers do not use compound drawables or drawable padding.
|
||||
//
|
||||
// Single line mode prevents entire words from being entirely clipped,
|
||||
// and instead only clips the portion of text that runs off.
|
||||
// The text should not clip due to the empty end padding,
|
||||
// but use the feature just in case.
|
||||
view.setSingleLine(true);
|
||||
// Center align to distribute the horizontal padding.
|
||||
view.setGravity(Gravity.CENTER);
|
||||
view.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
|
||||
ShapeDrawable shapeDrawable = ReturnYouTubeDislike.getLeftSeparatorDrawable();
|
||||
view.setCompoundDrawables(shapeDrawable, null, null, null);
|
||||
view.setCompoundDrawablePadding(ReturnYouTubeDislike.leftSeparatorShapePaddingPixels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static CharSequence updateRollingNumber(TextView view, CharSequence original) {
|
||||
try {
|
||||
if (!SettingsEnum.RYD_ENABLED.getBoolean()) {
|
||||
removeRollingNumberPatchChanges(view);
|
||||
return original;
|
||||
}
|
||||
// Called for all instances of RollingNumber, so must check if text is for a dislikes.
|
||||
// Text will already have the correct content but it's missing the drawable separators.
|
||||
if (!ReturnYouTubeDislike.isPreviouslyCreatedSegmentedSpan(original.toString())) {
|
||||
// The text is the video view count, upload time, or some other text.
|
||||
removeRollingNumberPatchChanges(view);
|
||||
return original;
|
||||
}
|
||||
|
||||
CharSequence replacement = rollingNumberSpan;
|
||||
if (replacement == null) {
|
||||
// User enabled RYD while a video was open,
|
||||
// or user opened/closed a Short while a regular video was opened.
|
||||
LogHelper.printDebug(() -> "Cannot update rolling number (field is null");
|
||||
removeRollingNumberPatchChanges(view);
|
||||
return original;
|
||||
}
|
||||
|
||||
// TextView does not display the tall left separator correctly,
|
||||
// as it goes outside the height bounds and messes up the layout.
|
||||
// Fix this by applying the left separator as a text view compound drawable.
|
||||
// This creates a new issue as the compound drawable is not taken into the
|
||||
// layout width sizing, but that is fixed in the span itself where it uses a blank
|
||||
// padding string that adds to the layout width but is later ignored during UI drawing.
|
||||
if (SettingsEnum.RYD_COMPACT_LAYOUT.getBoolean()) {
|
||||
// Do not apply any TextView changes, and text should always fit without clipping.
|
||||
removeRollingNumberPatchChanges(view);
|
||||
} else {
|
||||
addRollingNumberPatchChanges(view);
|
||||
}
|
||||
|
||||
// Remove any padding set by Rolling Number.
|
||||
view.setPadding(0, 0, 0, 0);
|
||||
|
||||
// When displaying dislikes, the rolling animation is not visually correct
|
||||
// and the dislikes always animate (even though the dislike count has not changed).
|
||||
// The animation is caused by an image span attached to the span,
|
||||
// and using only the modified segmented span prevents the animation from showing.
|
||||
return replacement;
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "updateRollingNumber failure", ex);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Non litho Shorts player.
|
||||
@ -301,7 +436,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
if (!SettingsEnum.RYD_SHORTS.getBoolean()) {
|
||||
// Must clear the data here, in case a new video was loaded while PlayerType
|
||||
// suggested the video was not a short (can happen when spoofing to an old app version).
|
||||
currentVideoData = null;
|
||||
clearData();
|
||||
return false;
|
||||
}
|
||||
LogHelper.printDebug(() -> "setShortsDislikes");
|
||||
@ -405,90 +540,59 @@ public class ReturnYouTubeDislikePatch {
|
||||
* Injection point. Uses 'playback response' video id hook to preload RYD.
|
||||
*/
|
||||
public static void preloadVideoId(@NonNull String videoId, boolean videoIsOpeningOrPlaying) {
|
||||
// Shorts shelf in home and subscription feed causes player response hook to be called,
|
||||
// and the 'is opening/playing' parameter will be false.
|
||||
// This hook will be called again when the Short is actually opened.
|
||||
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_ENABLED.getBoolean()) {
|
||||
return;
|
||||
try {
|
||||
// Shorts shelf in home and subscription feed causes player response hook to be called,
|
||||
// and the 'is opening/playing' parameter will be false.
|
||||
// This hook will be called again when the Short is actually opened.
|
||||
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_ENABLED.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
if (!SettingsEnum.RYD_SHORTS.getBoolean() && PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized()) {
|
||||
return;
|
||||
}
|
||||
if (videoId.equals(lastPrefetchedVideoId)) {
|
||||
return;
|
||||
}
|
||||
lastPrefetchedVideoId = videoId;
|
||||
LogHelper.printDebug(() -> "Prefetching RYD for video: " + videoId);
|
||||
ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "preloadVideoId failure", ex);
|
||||
}
|
||||
if (!SettingsEnum.RYD_SHORTS.getBoolean() && PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized()) {
|
||||
return;
|
||||
}
|
||||
if (videoId.equals(lastPrefetchedVideoId)) {
|
||||
return;
|
||||
}
|
||||
lastPrefetchedVideoId = videoId;
|
||||
LogHelper.printDebug(() -> "Prefetching RYD for video: " + videoId);
|
||||
ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Uses 'current playing' video id hook. Always called on main thread.
|
||||
*/
|
||||
public static void newVideoLoaded(@NonNull String videoId) {
|
||||
newVideoLoaded(videoId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called both on and off main thread.
|
||||
*
|
||||
* @param isShortsLithoVideoId If the video id is from {@link ReturnYouTubeDislikeFilterPatch}.
|
||||
* if true, then the video id can be null indicating the filter did
|
||||
* not find any video id.
|
||||
*/
|
||||
public static void newVideoLoaded(@Nullable String videoId, boolean isShortsLithoVideoId) {
|
||||
try {
|
||||
if (!SettingsEnum.RYD_ENABLED.getBoolean()) return;
|
||||
Objects.requireNonNull(videoId);
|
||||
|
||||
PlayerType currentPlayerType = PlayerType.getCurrent();
|
||||
final boolean isNoneHiddenOrSlidingMinimized = currentPlayerType.isNoneHiddenOrSlidingMinimized();
|
||||
if (isNoneHiddenOrSlidingMinimized && !SettingsEnum.RYD_SHORTS.getBoolean()) {
|
||||
// Must clear here, otherwise the wrong data can be used for a minimized regular video.
|
||||
currentVideoData = null;
|
||||
clearData();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isShortsLithoVideoId) {
|
||||
// Litho Shorts video.
|
||||
if (videoIdIsSame(lastLithoShortsVideoData, videoId)) {
|
||||
return;
|
||||
}
|
||||
if (videoId == null) {
|
||||
// Litho filter did not detect the video id. App is in incognito mode,
|
||||
// or the proto buffer structure was changed and the video id is no longer present.
|
||||
// Must clear both currently playing and last litho data otherwise the
|
||||
// next regular video may use the wrong data.
|
||||
LogHelper.printDebug(() -> "Litho filter did not find any video ids");
|
||||
currentVideoData = null;
|
||||
lastLithoShortsVideoData = null;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
return;
|
||||
}
|
||||
ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
videoData.setVideoIdIsShort(true);
|
||||
lastLithoShortsVideoData = videoData;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
} else {
|
||||
Objects.requireNonNull(videoId);
|
||||
// All other playback (including non-litho Shorts).
|
||||
if (videoIdIsSame(currentVideoData, videoId)) {
|
||||
return;
|
||||
}
|
||||
ReturnYouTubeDislike data = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
// Pre-emptively set the data to short status.
|
||||
// Required to prevent Shorts data from being used on a minimized video in incognito mode.
|
||||
if (isNoneHiddenOrSlidingMinimized) {
|
||||
data.setVideoIdIsShort(true);
|
||||
}
|
||||
currentVideoData = data;
|
||||
if (videoIdIsSame(currentVideoData, videoId)) {
|
||||
return;
|
||||
}
|
||||
LogHelper.printDebug(() -> "New video id: " + videoId + " playerType: " + currentPlayerType);
|
||||
|
||||
LogHelper.printDebug(() -> "New video id: " + videoId + " playerType: " + currentPlayerType
|
||||
+ " isShortsLithoHook: " + isShortsLithoVideoId);
|
||||
ReturnYouTubeDislike data = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
// Pre-emptively set the data to short status.
|
||||
// Required to prevent Shorts data from being used on a minimized video in incognito mode.
|
||||
if (isNoneHiddenOrSlidingMinimized) {
|
||||
data.setVideoIdIsShort(true);
|
||||
}
|
||||
currentVideoData = data;
|
||||
|
||||
// Current video id hook can be called out of order with the non litho Shorts text view hook.
|
||||
// Must manually update again here.
|
||||
if (!isShortsLithoVideoId && isNoneHiddenOrSlidingMinimized) {
|
||||
if (isNoneHiddenOrSlidingMinimized) {
|
||||
updateOnScreenShortsTextViews(true);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
@ -496,6 +600,26 @@ public class ReturnYouTubeDislikePatch {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setLastLithoShortsVideoId(@Nullable String videoId) {
|
||||
if (videoIdIsSame(lastLithoShortsVideoData, videoId)) {
|
||||
return;
|
||||
}
|
||||
if (videoId == null) {
|
||||
// Litho filter did not detect the video id. App is in incognito mode,
|
||||
// or the proto buffer structure was changed and the video id is no longer present.
|
||||
// Must clear both currently playing and last litho data otherwise the
|
||||
// next regular video may use the wrong data.
|
||||
LogHelper.printDebug(() -> "Litho filter did not find any video ids");
|
||||
clearData();
|
||||
return;
|
||||
}
|
||||
LogHelper.printDebug(() -> "New litho Shorts video id: " + videoId);
|
||||
ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
videoData.setVideoIdIsShort(true);
|
||||
lastLithoShortsVideoData = videoData;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
}
|
||||
|
||||
private static boolean videoIdIsSame(@Nullable ReturnYouTubeDislike fetch, @Nullable String videoId) {
|
||||
return (fetch == null && videoId == null)
|
||||
|| (fetch != null && fetch.getVideoId().equals(videoId));
|
||||
|
@ -0,0 +1,9 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public final class SlideToSeekPatch {
|
||||
public static boolean isSlideToSeekDisabled() {
|
||||
return !SettingsEnum.SLIDE_TO_SEEK.getBoolean();
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package app.revanced.integrations.patches.components;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.StringTrieSearch;
|
||||
|
||||
final class DescriptionComponentsFilter extends Filter {
|
||||
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
|
||||
public DescriptionComponentsFilter() {
|
||||
exceptions.addPatterns(
|
||||
"compact_channel",
|
||||
"description",
|
||||
"grid_video",
|
||||
"inline_expander",
|
||||
"metadata"
|
||||
);
|
||||
|
||||
final StringFilterGroup chapterSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_CHAPTERS,
|
||||
"macro_markers_carousel"
|
||||
);
|
||||
|
||||
final StringFilterGroup infoCardsSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_INFO_CARDS_SECTION,
|
||||
"infocards_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup gameSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_GAME_SECTION,
|
||||
"gaming_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup musicSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_MUSIC_SECTION,
|
||||
"music_section",
|
||||
"video_attributes_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup podcastSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_PODCAST_SECTION,
|
||||
"playlist_section"
|
||||
);
|
||||
|
||||
final StringFilterGroup transcriptSection = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_TRANSCIPT_SECTION,
|
||||
"transcript_section"
|
||||
);
|
||||
|
||||
pathFilterGroupList.addAll(
|
||||
chapterSection,
|
||||
infoCardsSection,
|
||||
gameSection,
|
||||
musicSection,
|
||||
podcastSection,
|
||||
transcriptSection
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
if (exceptions.matches(path)) return false;
|
||||
|
||||
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
}
|
||||
}
|
@ -2,10 +2,8 @@ package app.revanced.integrations.patches.components;
|
||||
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.StringTrieSearch;
|
||||
@ -13,6 +11,9 @@ import app.revanced.integrations.utils.StringTrieSearch;
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch();
|
||||
private static ByteArrayAsStringFilterGroup mixPlaylistsExceptions2;
|
||||
|
||||
private final CustomFilterGroup custom;
|
||||
|
||||
private static final ByteArrayAsStringFilterGroup mixPlaylists = new ByteArrayAsStringFilterGroup(
|
||||
@ -34,6 +35,16 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"library_recent_shelf"
|
||||
);
|
||||
|
||||
mixPlaylistsExceptions.addPatterns(
|
||||
"V.ED", // Playlist browse id.
|
||||
"java.lang.ref.WeakReference"
|
||||
);
|
||||
|
||||
mixPlaylistsExceptions2 = new ByteArrayAsStringFilterGroup(
|
||||
null,
|
||||
"cell_description_body"
|
||||
);
|
||||
|
||||
custom = new CustomFilterGroup(
|
||||
SettingsEnum.CUSTOM_FILTER,
|
||||
SettingsEnum.CUSTOM_FILTER_STRINGS
|
||||
@ -125,11 +136,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"quality_sheet_footer"
|
||||
);
|
||||
|
||||
final var chapters = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_CHAPTERS,
|
||||
"macro_markers_carousel"
|
||||
);
|
||||
|
||||
final var channelBar = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_CHANNEL_BAR,
|
||||
"channel_bar"
|
||||
@ -215,8 +221,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
|
||||
this.identifierFilterGroupList.addAll(
|
||||
graySeparator,
|
||||
chipsShelf,
|
||||
chapters
|
||||
chipsShelf
|
||||
);
|
||||
}
|
||||
|
||||
@ -238,18 +243,23 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Called from a different place then the other filters.
|
||||
*/
|
||||
public static boolean filterMixPlaylists(final byte[] bytes) {
|
||||
final boolean isMixPlaylistFiltered = mixPlaylists.check(bytes).isFiltered();
|
||||
public static boolean filterMixPlaylists(final Object conversionContext, final byte[] bytes) {
|
||||
// Prevent playlist items being hidden, if a mix playlist is present in it.
|
||||
if (mixPlaylistsExceptions.matches(conversionContext.toString()))
|
||||
return false;
|
||||
|
||||
if (isMixPlaylistFiltered)
|
||||
LogHelper.printDebug(() -> "Filtered mix playlist");
|
||||
if (!mixPlaylists.check(bytes).isFiltered()) return false;
|
||||
|
||||
// Prevent hiding the description of some videos accidentally.
|
||||
if (mixPlaylistsExceptions2.check(bytes).isFiltered()) return false;
|
||||
|
||||
LogHelper.printDebug(() -> "Filtered mix playlist");
|
||||
return true;
|
||||
|
||||
return isMixPlaylistFiltered;
|
||||
}
|
||||
|
||||
public static boolean showWatermark() {
|
||||
|
@ -2,7 +2,11 @@ package app.revanced.integrations.patches.components;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
// Abuse LithoFilter for CustomPlaybackSpeedPatch.
|
||||
import app.revanced.integrations.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||
|
||||
/**
|
||||
* Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}.
|
||||
*/
|
||||
public final class PlaybackSpeedMenuFilterPatch extends Filter {
|
||||
// Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
|
||||
public static volatile boolean isPlaybackSpeedMenuVisible;
|
||||
|
@ -93,7 +93,7 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
|
||||
// Must pass a null id to correctly clear out the current video data.
|
||||
// Otherwise if a Short is opened in non-incognito, then incognito is enabled and another Short is opened,
|
||||
// the new incognito Short will show the old prior data.
|
||||
ReturnYouTubeDislikePatch.newVideoLoaded(matchedVideoId, true);
|
||||
ReturnYouTubeDislikePatch.setLastLithoShortsVideoId(matchedVideoId);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -2,16 +2,19 @@ package app.revanced.integrations.patches.components;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.integrations.patches.playback.quality.RestoreOldVideoQualityMenuPatch;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
// Abuse LithoFilter for OldVideoQualityMenuPatch.
|
||||
/**
|
||||
* Abuse LithoFilter for {@link RestoreOldVideoQualityMenuPatch}.
|
||||
*/
|
||||
public final class VideoQualityMenuFilterPatch extends Filter {
|
||||
// Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
|
||||
public static volatile boolean isVideoQualityMenuVisible;
|
||||
|
||||
public VideoQualityMenuFilterPatch() {
|
||||
pathFilterGroupList.addAll(new StringFilterGroup(
|
||||
SettingsEnum.SHOW_OLD_VIDEO_QUALITY_MENU,
|
||||
SettingsEnum.RESTORE_OLD_VIDEO_QUALITY_MENU,
|
||||
"quick_quality_sheet_content.eml-js"
|
||||
));
|
||||
}
|
||||
|
@ -14,13 +14,14 @@ import app.revanced.integrations.utils.LogHelper;
|
||||
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
|
||||
* and a ListView in the old one.
|
||||
*/
|
||||
public final class OldVideoQualityMenuPatch {
|
||||
@SuppressWarnings("unused")
|
||||
public final class RestoreOldVideoQualityMenuPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
|
||||
if (!SettingsEnum.SHOW_OLD_VIDEO_QUALITY_MENU.getBoolean()) return;
|
||||
if (!SettingsEnum.RESTORE_OLD_VIDEO_QUALITY_MENU.getBoolean()) return;
|
||||
|
||||
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
|
||||
try {
|
||||
@ -32,6 +33,7 @@ public final class OldVideoQualityMenuPatch {
|
||||
View advancedQualityView = ((ViewGroup) recyclerView.getChildAt(0)).getChildAt(3);
|
||||
if (advancedQualityView != null) {
|
||||
// Click the "Advanced" quality menu to show the "old" quality menu.
|
||||
advancedQualityView.setSoundEffectsEnabled(false);
|
||||
advancedQualityView.performClick();
|
||||
}
|
||||
}
|
||||
@ -45,7 +47,7 @@ public final class OldVideoQualityMenuPatch {
|
||||
* Injection point. Only used if spoofing to an old app version.
|
||||
*/
|
||||
public static void showOldVideoQualityMenu(final ListView listView) {
|
||||
if (!SettingsEnum.SHOW_OLD_VIDEO_QUALITY_MENU.getBoolean()) return;
|
||||
if (!SettingsEnum.RESTORE_OLD_VIDEO_QUALITY_MENU.getBoolean()) return;
|
||||
|
||||
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
|
||||
@Override
|
@ -4,16 +4,14 @@ import android.preference.ListPreference;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import app.revanced.integrations.patches.components.PlaybackSpeedMenuFilterPatch;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CustomPlaybackSpeedPatch {
|
||||
/**
|
||||
* Maximum playback speed, exclusive value. Custom speeds must be less than this value.
|
||||
@ -111,7 +109,10 @@ public class CustomPlaybackSpeedPatch {
|
||||
|
||||
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
|
||||
// This only shows in phone layout.
|
||||
parentView4th.getChildAt(0).performClick();
|
||||
|
||||
final var touchInsidedView = parentView4th.getChildAt(0);
|
||||
touchInsidedView.setSoundEffectsEnabled(false);
|
||||
touchInsidedView.performClick();
|
||||
|
||||
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
|
||||
parentView3rd.setVisibility(View.GONE);
|
||||
|
@ -4,10 +4,19 @@ import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
public class SpoofAppVersionPatch {
|
||||
|
||||
private static final boolean SPOOF_APP_VERSION_ENABLED = SettingsEnum.SPOOF_APP_VERSION.getBoolean();
|
||||
private static final String SPOOF_APP_VERSION_TARGET = SettingsEnum.SPOOF_APP_VERSION_TARGET.getString();
|
||||
|
||||
/**
|
||||
* Injection point
|
||||
*/
|
||||
public static String getYouTubeVersionOverride(String version) {
|
||||
if (SettingsEnum.SPOOF_APP_VERSION.getBoolean()) {
|
||||
return SettingsEnum.SPOOF_APP_VERSION_TARGET.getString();
|
||||
}
|
||||
if (SPOOF_APP_VERSION_ENABLED) return SPOOF_APP_VERSION_TARGET;
|
||||
return version;
|
||||
}
|
||||
|
||||
public static boolean isSpoofingToEqualOrLessThan(String version) {
|
||||
return SPOOF_APP_VERSION_ENABLED && SPOOF_APP_VERSION_TARGET.compareTo(version) <= 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,10 @@ package app.revanced.integrations.patches.spoof;
|
||||
import static app.revanced.integrations.patches.spoof.requests.StoryboardRendererRequester.getStoryboardRenderer;
|
||||
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@ -51,11 +55,15 @@ public class SpoofSignaturePatch {
|
||||
|
||||
private static volatile Future<StoryboardRenderer> rendererFuture;
|
||||
|
||||
private static volatile boolean useOriginalStoryboardRenderer;
|
||||
|
||||
private static volatile boolean isPlayingShorts;
|
||||
|
||||
@Nullable
|
||||
private static StoryboardRenderer getRenderer() {
|
||||
if (rendererFuture != null) {
|
||||
try {
|
||||
return rendererFuture.get(5000, TimeUnit.MILLISECONDS);
|
||||
return rendererFuture.get(2000, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException ex) {
|
||||
LogHelper.printDebug(() -> "Could not get renderer (get timed out)");
|
||||
} catch (ExecutionException | InterruptedException ex) {
|
||||
@ -81,27 +89,38 @@ public class SpoofSignaturePatch {
|
||||
// Clip's player parameters contain a lot of information (e.g. video start and end time or whether it loops)
|
||||
// For this reason, the player parameters of a clip are usually very long (150~300 characters).
|
||||
// Clips are 60 seconds or less in length, so no spoofing.
|
||||
var isClip = parameters.length() > 150;
|
||||
if (isClip) return parameters;
|
||||
if (useOriginalStoryboardRenderer = parameters.length() > 150) return parameters;
|
||||
|
||||
// Shorts do not need to be spoofed.
|
||||
if (parameters.startsWith(SHORTS_PLAYER_PARAMETERS)) return parameters;
|
||||
if (useOriginalStoryboardRenderer = parameters.startsWith(SHORTS_PLAYER_PARAMETERS)) {
|
||||
isPlayingShorts = true;
|
||||
return parameters;
|
||||
}
|
||||
isPlayingShorts = false;
|
||||
|
||||
boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL && containsAny(parameters, AUTOPLAY_PARAMETERS);
|
||||
if (isPlayingFeed) return SettingsEnum.SPOOF_SIGNATURE_IN_FEED.getBoolean() ?
|
||||
// Prepend the scrim parameter to mute videos in feed.
|
||||
SCRIM_PARAMETER + INCOGNITO_PARAMETERS :
|
||||
// In order to prevent videos that are auto-played in feed to be added to history,
|
||||
// only spoof the parameter if the video is not playing in the feed.
|
||||
// This will cause playback issues in the feed, but it's better than manipulating the history.
|
||||
parameters;
|
||||
boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL
|
||||
&& containsAny(parameters, AUTOPLAY_PARAMETERS);
|
||||
if (isPlayingFeed) {
|
||||
if (useOriginalStoryboardRenderer = !SettingsEnum.SPOOF_SIGNATURE_IN_FEED.getBoolean()) {
|
||||
// Don't spoof the feed video playback. This will cause video playback issues,
|
||||
// but only if user continues watching for more than 1 minute.
|
||||
return parameters;
|
||||
}
|
||||
// Spoof the feed video. Video will show up in watch history and video subtitles are missing.
|
||||
fetchStoryboardRenderer();
|
||||
return SCRIM_PARAMETER + INCOGNITO_PARAMETERS;
|
||||
}
|
||||
|
||||
fetchStoryboardRenderer();
|
||||
|
||||
return INCOGNITO_PARAMETERS;
|
||||
}
|
||||
|
||||
private static void fetchStoryboardRenderer() {
|
||||
if (!SettingsEnum.SPOOF_STORYBOARD_RENDERER.getBoolean()) {
|
||||
lastPlayerResponseVideoId = null;
|
||||
rendererFuture = null;
|
||||
return;
|
||||
}
|
||||
String videoId = VideoInformation.getPlayerResponseVideoId();
|
||||
if (!videoId.equals(lastPlayerResponseVideoId)) {
|
||||
rendererFuture = ReVancedUtils.submitOnBackgroundThread(() -> getStoryboardRenderer(videoId));
|
||||
@ -115,11 +134,17 @@ public class SpoofSignaturePatch {
|
||||
getRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean getSeekbarThumbnailOverrideValue() {
|
||||
return SettingsEnum.SPOOF_SIGNATURE.getBoolean();
|
||||
private static String getStoryboardRendererSpec(String originalStoryboardRendererSpec,
|
||||
boolean returnNullIfLiveStream) {
|
||||
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean() && !useOriginalStoryboardRenderer) {
|
||||
StoryboardRenderer renderer = getRenderer();
|
||||
if (renderer != null) {
|
||||
if (returnNullIfLiveStream && renderer.isLiveStream()) return null;
|
||||
return renderer.getSpec();
|
||||
}
|
||||
}
|
||||
|
||||
return originalStoryboardRendererSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,19 +153,24 @@ public class SpoofSignaturePatch {
|
||||
*/
|
||||
@Nullable
|
||||
public static String getStoryboardRendererSpec(String originalStoryboardRendererSpec) {
|
||||
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
|
||||
StoryboardRenderer renderer = getRenderer();
|
||||
if (renderer != null) return renderer.getSpec();
|
||||
}
|
||||
return getStoryboardRendererSpec(originalStoryboardRendererSpec, false);
|
||||
}
|
||||
|
||||
return originalStoryboardRendererSpec;
|
||||
/**
|
||||
* Injection point.
|
||||
* Uses additional check to handle live streams.
|
||||
* Called from background threads and from the main thread.
|
||||
*/
|
||||
@Nullable
|
||||
public static String getStoryboardDecoderRendererSpec(String originalStoryboardRendererSpec) {
|
||||
return getStoryboardRendererSpec(originalStoryboardRendererSpec, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static int getRecommendedLevel(int originalLevel) {
|
||||
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
|
||||
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean() && !useOriginalStoryboardRenderer) {
|
||||
StoryboardRenderer renderer = getRenderer();
|
||||
if (renderer != null) {
|
||||
Integer recommendedLevel = renderer.getRecommendedLevel();
|
||||
@ -150,4 +180,30 @@ public class SpoofSignaturePatch {
|
||||
|
||||
return originalLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Forces seekbar to be shown for paid videos or
|
||||
* if {@link SettingsEnum#SPOOF_STORYBOARD_RENDERER} is not enabled.
|
||||
*/
|
||||
public static boolean getSeekbarThumbnailOverrideValue() {
|
||||
return SettingsEnum.SPOOF_SIGNATURE.getBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* @param view seekbar thumbnail view. Includes both shorts and regular videos.
|
||||
*/
|
||||
public static void seekbarImageViewCreated(ImageView view) {
|
||||
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()
|
||||
|| SettingsEnum.SPOOF_STORYBOARD_RENDERER.getBoolean()) {
|
||||
return;
|
||||
}
|
||||
if (isPlayingShorts) return;
|
||||
|
||||
view.setVisibility(View.GONE);
|
||||
// Also hide the border around the thumbnail (otherwise a 1 pixel wide bordered frame is visible).
|
||||
ViewGroup parentLayout = (ViewGroup) view.getParent();
|
||||
parentLayout.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class StoryboardRenderer {
|
||||
private final String spec;
|
||||
private final boolean isLiveStream;
|
||||
@Nullable
|
||||
private final Integer recommendedLevel;
|
||||
|
||||
public StoryboardRenderer(String spec, @Nullable Integer recommendedLevel) {
|
||||
public StoryboardRenderer(String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) {
|
||||
this.spec = spec;
|
||||
this.isLiveStream = isLiveStream;
|
||||
this.recommendedLevel = recommendedLevel;
|
||||
}
|
||||
|
||||
@ -20,6 +22,10 @@ public final class StoryboardRenderer {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public boolean isLiveStream() {
|
||||
return isLiveStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Recommended image quality level, or NULL if no recommendation exists.
|
||||
*/
|
||||
@ -32,7 +38,8 @@ public final class StoryboardRenderer {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StoryboardRenderer{" +
|
||||
"spec='" + spec + '\'' +
|
||||
"isLiveStream=" + isLiveStream +
|
||||
", spec='" + spec + '\'' +
|
||||
", recommendedLevel=" + recommendedLevel +
|
||||
'}';
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ public class StoryboardRendererRequester {
|
||||
|
||||
@Nullable
|
||||
private static JSONObject fetchPlayerResponse(@NonNull String requestBody) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
ReVancedUtils.verifyOffMainThread();
|
||||
Objects.requireNonNull(requestBody);
|
||||
@ -40,6 +41,8 @@ public class StoryboardRendererRequester {
|
||||
LogHelper.printException(() -> "API timed out", ex);
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "Failed to fetch storyboard URL", ex);
|
||||
} finally {
|
||||
LogHelper.printDebug(() -> "Request took: " + (System.currentTimeMillis() - startTime) + "ms");
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -72,14 +75,17 @@ public class StoryboardRendererRequester {
|
||||
@Nullable
|
||||
private static StoryboardRenderer getStoryboardRendererUsingResponse(@NonNull JSONObject playerResponse) {
|
||||
try {
|
||||
LogHelper.printDebug(() -> "Parsing response: " + playerResponse);
|
||||
final JSONObject storyboards = playerResponse.getJSONObject("storyboards");
|
||||
final String storyboardsRendererTag = storyboards.has("playerLiveStoryboardSpecRenderer")
|
||||
final boolean isLiveStream = storyboards.has("playerLiveStoryboardSpecRenderer");
|
||||
final String storyboardsRendererTag = isLiveStream
|
||||
? "playerLiveStoryboardSpecRenderer"
|
||||
: "playerStoryboardSpecRenderer";
|
||||
|
||||
final var rendererElement = storyboards.getJSONObject(storyboardsRendererTag);
|
||||
StoryboardRenderer renderer = new StoryboardRenderer(
|
||||
rendererElement.getString("spec"),
|
||||
isLiveStream,
|
||||
rendererElement.has("recommendedLevel")
|
||||
? rendererElement.getInt("recommendedLevel")
|
||||
: null
|
||||
|
@ -17,6 +17,7 @@ import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
|
||||
@ -69,7 +70,7 @@ public class ReturnYouTubeDislike {
|
||||
* Must be less than 5 seconds, as per:
|
||||
* https://developer.android.com/topic/performance/vitals/anr
|
||||
*/
|
||||
private static final long MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH = 4500;
|
||||
private static final long MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH = 4000;
|
||||
|
||||
/**
|
||||
* How long to retain successful RYD fetches.
|
||||
@ -84,9 +85,9 @@ public class ReturnYouTubeDislike {
|
||||
|
||||
/**
|
||||
* Unique placeholder character, used to detect if a segmented span already has dislikes added to it.
|
||||
* Can be any almost any non-visible character.
|
||||
* Must be something YouTube is unlikely to use, as it's searched for in all usage of Rolling Number.
|
||||
*/
|
||||
private static final char MIDDLE_SEPARATOR_CHARACTER = '\u2009'; // 'narrow space' character
|
||||
private static final char MIDDLE_SEPARATOR_CHARACTER = '◎'; // 'bullseye'
|
||||
|
||||
/**
|
||||
* Cached lookup of all video ids.
|
||||
@ -115,6 +116,12 @@ public class ReturnYouTubeDislike {
|
||||
private static final Rect leftSeparatorBounds;
|
||||
private static final Rect middleSeparatorBounds;
|
||||
|
||||
/**
|
||||
* Left separator horizontal padding for Rolling Number layout.
|
||||
*/
|
||||
public static final int leftSeparatorShapePaddingPixels;
|
||||
private static final ShapeDrawable leftSeparatorShape;
|
||||
|
||||
static {
|
||||
DisplayMetrics dp = Objects.requireNonNull(ReVancedUtils.getContext()).getResources().getDisplayMetrics();
|
||||
|
||||
@ -124,6 +131,11 @@ public class ReturnYouTubeDislike {
|
||||
final int middleSeparatorSize =
|
||||
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3.7f, dp);
|
||||
middleSeparatorBounds = new Rect(0, 0, middleSeparatorSize, middleSeparatorSize);
|
||||
|
||||
leftSeparatorShapePaddingPixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10.0f, dp);
|
||||
|
||||
leftSeparatorShape = new ShapeDrawable(new RectShape());
|
||||
leftSeparatorShape.setBounds(leftSeparatorBounds);
|
||||
}
|
||||
|
||||
private final String videoId;
|
||||
@ -167,19 +179,31 @@ public class ReturnYouTubeDislike {
|
||||
@GuardedBy("this")
|
||||
private SpannableString replacementLikeDislikeSpan;
|
||||
|
||||
private static int getSeparatorColor() {
|
||||
return ThemeHelper.isDarkTheme()
|
||||
? 0x33FFFFFF // transparent dark gray
|
||||
: 0xFFD9D9D9; // light gray
|
||||
}
|
||||
|
||||
public static ShapeDrawable getLeftSeparatorDrawable() {
|
||||
leftSeparatorShape.getPaint().setColor(getSeparatorColor());
|
||||
return leftSeparatorShape;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSegmentedButton If UI is using the segmented single UI component for both like and dislike.
|
||||
*/
|
||||
@NonNull
|
||||
private static SpannableString createDislikeSpan(@NonNull Spanned oldSpannable, boolean isSegmentedButton, @NonNull RYDVoteData voteData) {
|
||||
private static SpannableString createDislikeSpan(@NonNull Spanned oldSpannable,
|
||||
boolean isSegmentedButton,
|
||||
boolean isRollingNumber,
|
||||
@NonNull RYDVoteData voteData) {
|
||||
if (!isSegmentedButton) {
|
||||
// Simple replacement of 'dislike' with a number/percentage.
|
||||
return newSpannableWithDislikes(oldSpannable, voteData);
|
||||
}
|
||||
|
||||
// Note: Some locales use right to left layout (arabic, hebrew, etc),
|
||||
// and care must be taken to retain the existing RTL encoding character on the likes string,
|
||||
// otherwise text will incorrectly show as left to right.
|
||||
// Note: Some locales use right to left layout (Arabic, Hebrew, etc).
|
||||
// If making changes to this code, change device settings to a RTL language and verify layout is correct.
|
||||
String oldLikesString = oldSpannable.toString();
|
||||
|
||||
@ -202,21 +226,25 @@ public class ReturnYouTubeDislike {
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
final boolean compactLayout = SettingsEnum.RYD_COMPACT_LAYOUT.getBoolean();
|
||||
final int separatorColor = ThemeHelper.isDarkTheme()
|
||||
? 0x29AAAAAA // transparent dark gray
|
||||
: 0xFFD9D9D9; // light gray
|
||||
|
||||
if (!compactLayout) {
|
||||
// left separator
|
||||
String leftSeparatorString = ReVancedUtils.isRightToLeftTextLayout()
|
||||
? "\u200F " // u200F = right to left character
|
||||
: "\u200E "; // u200E = left to right character
|
||||
Spannable leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||
ShapeDrawable shapeDrawable = new ShapeDrawable(new RectShape());
|
||||
shapeDrawable.getPaint().setColor(separatorColor);
|
||||
shapeDrawable.setBounds(leftSeparatorBounds);
|
||||
leftSeparatorSpan.setSpan(new VerticallyCenteredImageSpan(shapeDrawable), 1, 2,
|
||||
Spannable.SPAN_INCLUSIVE_EXCLUSIVE); // drawable cannot overwrite RTL or LTR character
|
||||
? "\u200F" // u200F = right to left character
|
||||
: "\u200E"; // u200E = left to right character
|
||||
final Spannable leftSeparatorSpan;
|
||||
if (isRollingNumber) {
|
||||
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||
} else {
|
||||
leftSeparatorString += " ";
|
||||
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||
// Styling spans cannot overwrite RTL or LTR character.
|
||||
leftSeparatorSpan.setSpan(
|
||||
new VerticallyCenteredImageSpan(getLeftSeparatorDrawable(), false),
|
||||
1, 2, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
leftSeparatorSpan.setSpan(
|
||||
new FixedWidthEmptySpan(leftSeparatorShapePaddingPixels),
|
||||
2, 3, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
builder.append(leftSeparatorSpan);
|
||||
}
|
||||
|
||||
@ -230,21 +258,41 @@ public class ReturnYouTubeDislike {
|
||||
final int shapeInsertionIndex = middleSeparatorString.length() / 2;
|
||||
Spannable middleSeparatorSpan = new SpannableString(middleSeparatorString);
|
||||
ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape());
|
||||
shapeDrawable.getPaint().setColor(separatorColor);
|
||||
shapeDrawable.getPaint().setColor(getSeparatorColor());
|
||||
shapeDrawable.setBounds(middleSeparatorBounds);
|
||||
middleSeparatorSpan.setSpan(new VerticallyCenteredImageSpan(shapeDrawable), shapeInsertionIndex, shapeInsertionIndex + 1,
|
||||
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
// Use original text width if using compact layout with Rolling Number,
|
||||
// as there is no empty padding to allow any layout width differences.
|
||||
middleSeparatorSpan.setSpan(
|
||||
new VerticallyCenteredImageSpan(shapeDrawable, isRollingNumber && compactLayout),
|
||||
shapeInsertionIndex, shapeInsertionIndex + 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
builder.append(middleSeparatorSpan);
|
||||
|
||||
// dislikes
|
||||
builder.append(newSpannableWithDislikes(oldSpannable, voteData));
|
||||
|
||||
// Add some padding for Rolling Number segmented span.
|
||||
// Use an empty width span, as the layout uses the measured text width and not the
|
||||
// actual span width. So adding padding and then removing it while drawing gives some
|
||||
// extra wiggle room for the left separator drawable (which is not included in layout width).
|
||||
if (isRollingNumber && !compactLayout) {
|
||||
// To test this, set the device system font to the smallest available.
|
||||
// If text clipping still occurs, then increase the number of padding spaces below.
|
||||
// Any extra width will be padded around the like/dislike string
|
||||
// as it's set to center text alignment.
|
||||
Spannable rightPaddingString = new SpannableString(" ");
|
||||
rightPaddingString.setSpan(new FixedWidthEmptySpan(0), 0,
|
||||
rightPaddingString.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
builder.append(rightPaddingString);
|
||||
}
|
||||
|
||||
return new SpannableString(builder);
|
||||
}
|
||||
|
||||
// Alternatively, this could check if the span contains one of the custom created spans, but this is simple and quick.
|
||||
private static boolean isPreviouslyCreatedSegmentedSpan(@NonNull Spanned span) {
|
||||
return span.toString().indexOf(MIDDLE_SEPARATOR_CHARACTER) != -1;
|
||||
/**
|
||||
* @return If the text is likely for a previously created likes/dislikes segmented span.
|
||||
*/
|
||||
public static boolean isPreviouslyCreatedSegmentedSpan(@NonNull String text) {
|
||||
return text.indexOf(MIDDLE_SEPARATOR_CHARACTER) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -429,8 +477,10 @@ public class ReturnYouTubeDislike {
|
||||
* @return the replacement span containing dislikes, or the original span if RYD is not available.
|
||||
*/
|
||||
@NonNull
|
||||
public synchronized Spanned getDislikesSpanForRegularVideo(@NonNull Spanned original, boolean isSegmentedButton) {
|
||||
return waitForFetchAndUpdateReplacementSpan(original, isSegmentedButton, false);
|
||||
public synchronized Spanned getDislikesSpanForRegularVideo(@NonNull Spanned original,
|
||||
boolean isSegmentedButton,
|
||||
boolean isRollingNumber) {
|
||||
return waitForFetchAndUpdateReplacementSpan(original, isSegmentedButton, isRollingNumber,false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -438,12 +488,13 @@ public class ReturnYouTubeDislike {
|
||||
*/
|
||||
@NonNull
|
||||
public synchronized Spanned getDislikeSpanForShort(@NonNull Spanned original) {
|
||||
return waitForFetchAndUpdateReplacementSpan(original, false, true);
|
||||
return waitForFetchAndUpdateReplacementSpan(original, false, false, true);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Spanned waitForFetchAndUpdateReplacementSpan(@NonNull Spanned original,
|
||||
boolean isSegmentedButton,
|
||||
boolean isRollingNumber,
|
||||
boolean spanIsForShort) {
|
||||
try {
|
||||
RYDVoteData votingData = getFetchData(MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH);
|
||||
@ -481,7 +532,7 @@ public class ReturnYouTubeDislike {
|
||||
return replacementLikeDislikeSpan;
|
||||
}
|
||||
}
|
||||
if (isSegmentedButton && isPreviouslyCreatedSegmentedSpan(original)) {
|
||||
if (isSegmentedButton && isPreviouslyCreatedSegmentedSpan(original.toString())) {
|
||||
// need to recreate using original, as original has prior outdated dislike values
|
||||
if (originalDislikeSpan == null) {
|
||||
// Should never happen.
|
||||
@ -497,7 +548,7 @@ public class ReturnYouTubeDislike {
|
||||
votingData.updateUsingVote(userVote);
|
||||
}
|
||||
originalDislikeSpan = original;
|
||||
replacementLikeDislikeSpan = createDislikeSpan(original, isSegmentedButton, votingData);
|
||||
replacementLikeDislikeSpan = createDislikeSpan(original, isSegmentedButton, isRollingNumber, votingData);
|
||||
LogHelper.printDebug(() -> "Replaced: '" + originalDislikeSpan + "' with: '"
|
||||
+ replacementLikeDislikeSpan + "'" + " using video: " + videoId);
|
||||
|
||||
@ -567,9 +618,44 @@ public class ReturnYouTubeDislike {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles a Spannable with an empty fixed width.
|
||||
*/
|
||||
class FixedWidthEmptySpan extends ReplacementSpan {
|
||||
final int fixedWidth;
|
||||
/**
|
||||
* @param fixedWith Fixed width in screen pixels.
|
||||
*/
|
||||
FixedWidthEmptySpan(int fixedWith) {
|
||||
this.fixedWidth = fixedWith;
|
||||
if (fixedWith < 0) throw new IllegalArgumentException();
|
||||
}
|
||||
@Override
|
||||
public int getSize(@NonNull Paint paint, @NonNull CharSequence text,
|
||||
int start, int end, @Nullable Paint.FontMetricsInt fontMetrics) {
|
||||
return fixedWidth;
|
||||
}
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end,
|
||||
float x, int top, int y, int bottom, @NonNull Paint paint) {
|
||||
// Nothing to draw.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertically centers a Spanned Drawable.
|
||||
*/
|
||||
class VerticallyCenteredImageSpan extends ImageSpan {
|
||||
public VerticallyCenteredImageSpan(Drawable drawable) {
|
||||
final boolean useOriginalWidth;
|
||||
|
||||
/**
|
||||
* @param useOriginalWidth Use the original layout width of the text this span is applied to,
|
||||
* and not the bounds of the Drawable. Drawable is always displayed using it's own bounds,
|
||||
* and this setting only affects the layout width of the entire span.
|
||||
*/
|
||||
public VerticallyCenteredImageSpan(Drawable drawable, boolean useOriginalWidth) {
|
||||
super(drawable);
|
||||
this.useOriginalWidth = useOriginalWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -581,13 +667,17 @@ class VerticallyCenteredImageSpan extends ImageSpan {
|
||||
Paint.FontMetricsInt paintMetrics = paint.getFontMetricsInt();
|
||||
final int fontHeight = paintMetrics.descent - paintMetrics.ascent;
|
||||
final int drawHeight = bounds.bottom - bounds.top;
|
||||
final int halfDrawHeight = drawHeight / 2;
|
||||
final int yCenter = paintMetrics.ascent + fontHeight / 2;
|
||||
|
||||
fontMetrics.ascent = yCenter - drawHeight / 2;
|
||||
fontMetrics.ascent = yCenter - halfDrawHeight;
|
||||
fontMetrics.top = fontMetrics.ascent;
|
||||
fontMetrics.bottom = yCenter + drawHeight / 2;
|
||||
fontMetrics.bottom = yCenter + halfDrawHeight;
|
||||
fontMetrics.descent = fontMetrics.bottom;
|
||||
}
|
||||
if (useOriginalWidth) {
|
||||
return (int) paint.measureText(text, start, end);
|
||||
}
|
||||
return bounds.right;
|
||||
}
|
||||
|
||||
@ -600,8 +690,13 @@ class VerticallyCenteredImageSpan extends ImageSpan {
|
||||
final int fontHeight = paintMetrics.descent - paintMetrics.ascent;
|
||||
final int yCenter = y + paintMetrics.descent - fontHeight / 2;
|
||||
final Rect drawBounds = drawable.getBounds();
|
||||
float translateX = x;
|
||||
if (useOriginalWidth) {
|
||||
// Horizontally center the drawable in the same space as the original text.
|
||||
translateX += (paint.measureText(text, start, end) - (drawBounds.right - drawBounds.left)) / 2;
|
||||
}
|
||||
final int translateY = yCenter - (drawBounds.bottom - drawBounds.top) / 2;
|
||||
canvas.translate(x, translateY);
|
||||
canvas.translate(translateX, translateY);
|
||||
drawable.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public class ReturnYouTubeDislikeApi {
|
||||
* {@link #fetchVotes(String)} HTTP read timeout.
|
||||
* To locally debug and force timeouts, change this to a very small number (ie: 100)
|
||||
*/
|
||||
private static final int API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS = 5 * 1000; // 5 Seconds.
|
||||
private static final int API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS = 4 * 1000; // 4 Seconds.
|
||||
|
||||
/**
|
||||
* Default connection and response timeout for voting and registration.
|
||||
|
@ -32,7 +32,8 @@ public enum SettingsEnum {
|
||||
|
||||
// Video
|
||||
HDR_AUTO_BRIGHTNESS("revanced_hdr_auto_brightness", BOOLEAN, TRUE),
|
||||
SHOW_OLD_VIDEO_QUALITY_MENU("revanced_show_old_video_quality_menu", BOOLEAN, TRUE),
|
||||
@Deprecated SHOW_OLD_VIDEO_QUALITY_MENU("revanced_show_old_video_quality_menu", BOOLEAN, TRUE),
|
||||
RESTORE_OLD_VIDEO_QUALITY_MENU("revanced_restore_old_video_quality_menu", BOOLEAN, TRUE),
|
||||
REMEMBER_VIDEO_QUALITY_LAST_SELECTED("revanced_remember_video_quality_last_selected", BOOLEAN, TRUE),
|
||||
VIDEO_QUALITY_DEFAULT_WIFI("revanced_video_quality_default_wifi", INTEGER, -2),
|
||||
VIDEO_QUALITY_DEFAULT_MOBILE("revanced_video_quality_default_mobile", INTEGER, -2),
|
||||
@ -44,41 +45,112 @@ public enum SettingsEnum {
|
||||
// Ads
|
||||
HIDE_BUTTONED_ADS("revanced_hide_buttoned_ads", BOOLEAN, TRUE),
|
||||
HIDE_GENERAL_ADS("revanced_hide_general_ads", BOOLEAN, TRUE),
|
||||
HIDE_GET_PREMIUM("revanced_hide_get_premium", BOOLEAN, TRUE),
|
||||
HIDE_HIDE_LATEST_POSTS("revanced_hide_latest_posts_ads", BOOLEAN, TRUE),
|
||||
HIDE_MERCHANDISE_BANNERS("revanced_hide_merchandise_banners", BOOLEAN, TRUE),
|
||||
HIDE_PAID_CONTENT("revanced_hide_paid_content_ads", BOOLEAN, TRUE),
|
||||
HIDE_PRODUCTS_BANNER("revanced_hide_products_banner", BOOLEAN, TRUE),
|
||||
HIDE_SELF_SPONSOR("revanced_hide_self_sponsor_ads", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_ADS("revanced_hide_video_ads", BOOLEAN, TRUE, true),
|
||||
CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE),
|
||||
CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)),
|
||||
HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE),
|
||||
|
||||
// Layout
|
||||
ALT_THUMBNAIL("revanced_alt_thumbnail", BOOLEAN, FALSE),
|
||||
ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2, parents(ALT_THUMBNAIL)),
|
||||
ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE, parents(ALT_THUMBNAIL)),
|
||||
CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE),
|
||||
CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)),
|
||||
DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true),
|
||||
DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE),
|
||||
DISABLE_SUGGESTED_VIDEO_END_SCREEN("revanced_disable_suggested_video_end_screen", BOOLEAN, TRUE),
|
||||
GRADIENT_LOADING_SCREEN("revanced_gradient_loading_screen", BOOLEAN, FALSE),
|
||||
HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true),
|
||||
HIDE_ARTIST_CARDS("revanced_hide_artist_cards", BOOLEAN, FALSE),
|
||||
HIDE_AUTOPLAY_BUTTON("revanced_hide_autoplay_button", BOOLEAN, TRUE, true),
|
||||
HIDE_BREAKING_NEWS("revanced_hide_breaking_news", BOOLEAN, TRUE, true),
|
||||
HIDE_CAPTIONS_BUTTON("revanced_hide_captions_button", BOOLEAN, FALSE),
|
||||
HIDE_CAST_BUTTON("revanced_hide_cast_button", BOOLEAN, TRUE, true),
|
||||
HIDE_CHANNEL_BAR("revanced_hide_channel_bar", BOOLEAN, FALSE),
|
||||
HIDE_CHANNEL_MEMBER_SHELF("revanced_hide_channel_member_shelf", BOOLEAN, TRUE),
|
||||
HIDE_EXPANDABLE_CHIP("revanced_hide_expandable_chip", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_QUALITY_MENU_FOOTER("revanced_hide_video_quality_menu_footer", BOOLEAN, TRUE),
|
||||
HIDE_CHAPTERS("revanced_hide_chapters", BOOLEAN, TRUE),
|
||||
HIDE_CHIPS_SHELF("revanced_hide_chips_shelf", BOOLEAN, TRUE),
|
||||
HIDE_COMMENTS_SECTION("revanced_hide_comments_section", BOOLEAN, FALSE, true),
|
||||
HIDE_COMMUNITY_GUIDELINES("revanced_hide_community_guidelines", BOOLEAN, TRUE),
|
||||
HIDE_COMMUNITY_POSTS("revanced_hide_community_posts", BOOLEAN, FALSE),
|
||||
HIDE_COMPACT_BANNER("revanced_hide_compact_banner", BOOLEAN, TRUE),
|
||||
HIDE_CREATE_BUTTON("revanced_hide_create_button", BOOLEAN, TRUE, true),
|
||||
HIDE_CROWDFUNDING_BOX("revanced_hide_crowdfunding_box", BOOLEAN, FALSE, true),
|
||||
HIDE_EMAIL_ADDRESS("revanced_hide_email_address", BOOLEAN, FALSE),
|
||||
HIDE_EMERGENCY_BOX("revanced_hide_emergency_box", BOOLEAN, TRUE),
|
||||
HIDE_ENDSCREEN_CARDS("revanced_hide_endscreen_cards", BOOLEAN, TRUE),
|
||||
HIDE_EXPANDABLE_CHIP("revanced_hide_expandable_chip", BOOLEAN, TRUE),
|
||||
HIDE_FEED_SURVEY("revanced_hide_feed_survey", BOOLEAN, TRUE),
|
||||
HIDE_FILTER_BAR_FEED_IN_FEED("revanced_hide_filter_bar_feed_in_feed", BOOLEAN, FALSE, true),
|
||||
HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS("revanced_hide_filter_bar_feed_in_related_videos", BOOLEAN, FALSE, true),
|
||||
HIDE_FILTER_BAR_FEED_IN_SEARCH("revanced_hide_filter_bar_feed_in_search", BOOLEAN, FALSE, true),
|
||||
HIDE_FLOATING_MICROPHONE_BUTTON("revanced_hide_floating_microphone_button", BOOLEAN, TRUE, true),
|
||||
HIDE_FULLSCREEN_PANELS("revanced_hide_fullscreen_panels", BOOLEAN, TRUE, true),
|
||||
HIDE_GRAY_SEPARATOR("revanced_hide_gray_separator", BOOLEAN, TRUE),
|
||||
HIDE_TIMED_REACTIONS("revanced_hide_timed_reactions", BOOLEAN, TRUE),
|
||||
HIDE_SEARCH_RESULT_SHELF_HEADER("revanced_hide_search_result_shelf_header", BOOLEAN, FALSE),
|
||||
HIDE_NOTIFY_ME_BUTTON("revanced_hide_notify_me_button", BOOLEAN, TRUE),
|
||||
HIDE_JOIN_MEMBERSHIP_BUTTON("revanced_hide_join_membership_button", BOOLEAN, TRUE),
|
||||
HIDE_HIDE_CHANNEL_GUIDELINES("revanced_hide_channel_guidelines", BOOLEAN, TRUE),
|
||||
HIDE_IMAGE_SHELF("revanced_hide_image_shelf", BOOLEAN, TRUE),
|
||||
HIDE_HIDE_INFO_PANELS("revanced_hide_info_panels", BOOLEAN, TRUE),
|
||||
HIDE_HOME_BUTTON("revanced_hide_home_button", BOOLEAN, FALSE, true),
|
||||
HIDE_IMAGE_SHELF("revanced_hide_image_shelf", BOOLEAN, TRUE),
|
||||
HIDE_INFO_CARDS("revanced_hide_info_cards", BOOLEAN, TRUE),
|
||||
HIDE_JOIN_MEMBERSHIP_BUTTON("revanced_hide_join_membership_button", BOOLEAN, TRUE),
|
||||
HIDE_LOAD_MORE_BUTTON("revanced_hide_load_more_button", BOOLEAN, TRUE, true),
|
||||
HIDE_MEDICAL_PANELS("revanced_hide_medical_panels", BOOLEAN, TRUE),
|
||||
HIDE_MERCHANDISE_BANNERS("revanced_hide_merchandise_banners", BOOLEAN, TRUE),
|
||||
HIDE_MIX_PLAYLISTS("revanced_hide_mix_playlists", BOOLEAN, TRUE),
|
||||
HIDE_MOVIES_SECTION("revanced_hide_movies_section", BOOLEAN, TRUE),
|
||||
HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES("revanced_hide_subscribers_community_guidelines", BOOLEAN, TRUE),
|
||||
HIDE_PRODUCTS_BANNER("revanced_hide_products_banner", BOOLEAN, TRUE),
|
||||
HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE),
|
||||
HIDE_NOTIFY_ME_BUTTON("revanced_hide_notify_me_button", BOOLEAN, TRUE),
|
||||
HIDE_PLAYER_BUTTONS("revanced_hide_player_buttons", BOOLEAN, FALSE),
|
||||
HIDE_PREVIEW_COMMENT("revanced_hide_preview_comment", BOOLEAN, FALSE, true),
|
||||
HIDE_QUICK_ACTIONS("revanced_hide_quick_actions", BOOLEAN, FALSE),
|
||||
HIDE_RELATED_VIDEOS("revanced_hide_related_videos", BOOLEAN, FALSE),
|
||||
HIDE_MIX_PLAYLISTS("revanced_hide_mix_playlists", BOOLEAN, TRUE),
|
||||
HIDE_SEARCH_RESULT_SHELF_HEADER("revanced_hide_search_result_shelf_header", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_BUTTON("revanced_hide_shorts_button", BOOLEAN, TRUE, true),
|
||||
HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES("revanced_hide_subscribers_community_guidelines", BOOLEAN, TRUE),
|
||||
HIDE_SUBSCRIPTIONS_BUTTON("revanced_hide_subscriptions_button", BOOLEAN, FALSE, true),
|
||||
HIDE_TIMED_REACTIONS("revanced_hide_timed_reactions", BOOLEAN, TRUE),
|
||||
HIDE_TIMESTAMP("revanced_hide_timestamp", BOOLEAN, FALSE),
|
||||
@Deprecated HIDE_VIDEO_WATERMARK("revanced_hide_video_watermark", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_CHANNEL_WATERMARK("revanced_hide_channel_watermark", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_QUALITY_MENU_FOOTER("revanced_hide_video_quality_menu_footer", BOOLEAN, TRUE),
|
||||
PLAYER_OVERLAY_OPACITY("revanced_player_overlay_opacity", INTEGER, 100, true),
|
||||
PLAYER_POPUP_PANELS("revanced_hide_player_popup_panels", BOOLEAN, FALSE),
|
||||
SPOOF_APP_VERSION("revanced_spoof_app_version", BOOLEAN, FALSE, true, "revanced_spoof_app_version_user_dialog_message"),
|
||||
SPOOF_APP_VERSION_TARGET("revanced_spoof_app_version_target", STRING, "17.08.35", true, parents(SPOOF_APP_VERSION)),
|
||||
SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON("revanced_switch_create_with_notifications_button", BOOLEAN, TRUE, true),
|
||||
TABLET_LAYOUT("revanced_tablet_layout", BOOLEAN, FALSE, true, "revanced_tablet_layout_user_dialog_message"),
|
||||
USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true),
|
||||
WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true),
|
||||
// Description
|
||||
HIDE_CHAPTERS("revanced_hide_chapters", BOOLEAN, TRUE),
|
||||
HIDE_INFO_CARDS_SECTION("revanced_hide_info_cards_section", BOOLEAN, TRUE),
|
||||
HIDE_GAME_SECTION("revanced_hide_game_section", BOOLEAN, TRUE),
|
||||
HIDE_MUSIC_SECTION("revanced_hide_music_section", BOOLEAN, TRUE),
|
||||
HIDE_PODCAST_SECTION("revanced_hide_podcast_section", BOOLEAN, TRUE),
|
||||
HIDE_TRANSCIPT_SECTION("revanced_hide_transcript_section", BOOLEAN, TRUE),
|
||||
|
||||
// Shorts
|
||||
HIDE_SHORTS("revanced_hide_shorts", BOOLEAN, FALSE, true),
|
||||
HIDE_SHORTS_JOIN_BUTTON("revanced_hide_shorts_join_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SUBSCRIBE_BUTTON("revanced_hide_shorts_subscribe_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SUBSCRIBE_BUTTON_PAUSED("revanced_hide_shorts_subscribe_button_paused", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_THANKS_BUTTON("revanced_hide_shorts_thanks_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_COMMENTS_BUTTON("revanced_hide_shorts_comments_button", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_REMIX_BUTTON("revanced_hide_shorts_remix_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SHARE_BUTTON("revanced_hide_shorts_share_button", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_INFO_PANEL("revanced_hide_shorts_info_panel", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SOUND_BUTTON("revanced_hide_shorts_sound_button", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_CHANNEL_BAR("revanced_hide_shorts_channel_bar", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_NAVIGATION_BAR("revanced_hide_shorts_navigation_bar", BOOLEAN, TRUE, true),
|
||||
|
||||
// Seekbar
|
||||
@Deprecated ENABLE_OLD_SEEKBAR_THUMBNAILS("revanced_enable_old_seekbar_thumbnails", BOOLEAN, TRUE),
|
||||
RESTORE_OLD_SEEKBAR_THUMBNAILS("revanced_restore_old_seekbar_thumbnails", BOOLEAN, TRUE),
|
||||
HIDE_SEEKBAR("revanced_hide_seekbar", BOOLEAN, FALSE),
|
||||
HIDE_SEEKBAR_THUMBNAIL("revanced_hide_seekbar_thumbnail", BOOLEAN, FALSE),
|
||||
SEEKBAR_CUSTOM_COLOR("revanced_seekbar_custom_color", BOOLEAN, TRUE, true),
|
||||
SEEKBAR_CUSTOM_COLOR_VALUE("revanced_seekbar_custom_color_value", STRING, "#FF0000", true, parents(SEEKBAR_CUSTOM_COLOR)),
|
||||
|
||||
// Action buttons
|
||||
HIDE_LIKE_DISLIKE_BUTTON("revanced_hide_like_dislike_button", BOOLEAN, FALSE),
|
||||
@ -92,69 +164,7 @@ public enum SettingsEnum {
|
||||
HIDE_PLAYLIST_BUTTON("revanced_hide_playlist_button", BOOLEAN, FALSE),
|
||||
HIDE_SHOP_BUTTON("revanced_hide_shop_button", BOOLEAN, TRUE),
|
||||
|
||||
// Layout
|
||||
PLAYER_OVERLAY_OPACITY("revanced_player_overlay_opacity", INTEGER, 100, true),
|
||||
DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE),
|
||||
HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true),
|
||||
HIDE_ARTIST_CARDS("revanced_hide_artist_cards", BOOLEAN, FALSE),
|
||||
HIDE_AUTOPLAY_BUTTON("revanced_hide_autoplay_button", BOOLEAN, TRUE, true),
|
||||
HIDE_BREAKING_NEWS("revanced_hide_breaking_news", BOOLEAN, TRUE, true),
|
||||
HIDE_CAPTIONS_BUTTON("revanced_hide_captions_button", BOOLEAN, FALSE),
|
||||
HIDE_CAST_BUTTON("revanced_hide_cast_button", BOOLEAN, TRUE, true),
|
||||
HIDE_COMMENTS_SECTION("revanced_hide_comments_section", BOOLEAN, FALSE, true),
|
||||
HIDE_CREATE_BUTTON("revanced_hide_create_button", BOOLEAN, TRUE, true),
|
||||
HIDE_CHIPS_SHELF("revanced_hide_chips_shelf", BOOLEAN, TRUE),
|
||||
HIDE_CROWDFUNDING_BOX("revanced_hide_crowdfunding_box", BOOLEAN, FALSE, true),
|
||||
HIDE_EMAIL_ADDRESS("revanced_hide_email_address", BOOLEAN, FALSE),
|
||||
HIDE_ENDSCREEN_CARDS("revanced_hide_endscreen_cards", BOOLEAN, TRUE),
|
||||
HIDE_FLOATING_MICROPHONE_BUTTON("revanced_hide_floating_microphone_button", BOOLEAN, TRUE, true),
|
||||
HIDE_FULLSCREEN_PANELS("revanced_hide_fullscreen_panels", BOOLEAN, TRUE, true),
|
||||
HIDE_GET_PREMIUM("revanced_hide_get_premium", BOOLEAN, TRUE),
|
||||
HIDE_INFO_CARDS("revanced_hide_info_cards", BOOLEAN, TRUE),
|
||||
HIDE_LOAD_MORE_BUTTON("revanced_hide_load_more_button", BOOLEAN, TRUE, true),
|
||||
HIDE_PLAYER_BUTTONS("revanced_hide_player_buttons", BOOLEAN, FALSE),
|
||||
HIDE_PREVIEW_COMMENT("revanced_hide_preview_comment", BOOLEAN, FALSE, true),
|
||||
HIDE_SEEKBAR("revanced_hide_seekbar", BOOLEAN, FALSE),
|
||||
HIDE_SEEKBAR_THUMBNAIL("revanced_hide_seekbar_thumbnail", BOOLEAN, FALSE),
|
||||
HIDE_HOME_BUTTON("revanced_hide_home_button", BOOLEAN, FALSE, true),
|
||||
HIDE_SHORTS_BUTTON("revanced_hide_shorts_button", BOOLEAN, TRUE, true),
|
||||
HIDE_SUBSCRIPTIONS_BUTTON("revanced_hide_subscriptions_button", BOOLEAN, FALSE, true),
|
||||
HIDE_TIMESTAMP("revanced_hide_timestamp", BOOLEAN, FALSE),
|
||||
HIDE_VIDEO_WATERMARK("revanced_hide_video_watermark", BOOLEAN, TRUE),
|
||||
HIDE_VIDEO_CHANNEL_WATERMARK("revanced_hide_channel_watermark", BOOLEAN, TRUE),
|
||||
PLAYER_POPUP_PANELS("revanced_hide_player_popup_panels", BOOLEAN, FALSE),
|
||||
SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON("revanced_switch_create_with_notifications_button", BOOLEAN, TRUE, true),
|
||||
SPOOF_APP_VERSION("revanced_spoof_app_version", BOOLEAN, FALSE, true, "revanced_spoof_app_version_user_dialog_message"),
|
||||
SPOOF_APP_VERSION_TARGET("revanced_spoof_app_version_target", STRING, "17.08.35", true, parents(SPOOF_APP_VERSION)),
|
||||
USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true),
|
||||
TABLET_LAYOUT("revanced_tablet_layout", BOOLEAN, FALSE, true, "revanced_tablet_layout_user_dialog_message"),
|
||||
WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true),
|
||||
GRADIENT_LOADING_SCREEN("revanced_gradient_loading_screen", BOOLEAN, FALSE),
|
||||
SEEKBAR_CUSTOM_COLOR("revanced_seekbar_custom_color", BOOLEAN, TRUE, true),
|
||||
SEEKBAR_CUSTOM_COLOR_VALUE("revanced_seekbar_custom_color_value", STRING, "#FF0000", true, parents(SEEKBAR_CUSTOM_COLOR)),
|
||||
HIDE_FILTER_BAR_FEED_IN_FEED("revanced_hide_filter_bar_feed_in_feed", BOOLEAN, FALSE, true),
|
||||
HIDE_FILTER_BAR_FEED_IN_SEARCH("revanced_hide_filter_bar_feed_in_search", BOOLEAN, FALSE, true),
|
||||
HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS("revanced_hide_filter_bar_feed_in_related_videos", BOOLEAN, FALSE, true),
|
||||
HIDE_SHORTS_JOIN_BUTTON("revanced_hide_shorts_join_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SUBSCRIBE_BUTTON("revanced_hide_shorts_subscribe_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SUBSCRIBE_BUTTON_PAUSED("revanced_hide_shorts_subscribe_button_paused", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_THANKS_BUTTON("revanced_hide_shorts_thanks_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_COMMENTS_BUTTON("revanced_hide_shorts_comments_button", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_REMIX_BUTTON("revanced_hide_shorts_remix_button", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SHARE_BUTTON("revanced_hide_shorts_share_button", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_INFO_PANEL("revanced_hide_shorts_info_panel", BOOLEAN, TRUE),
|
||||
HIDE_SHORTS_SOUND_BUTTON("revanced_hide_shorts_sound_button", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_CHANNEL_BAR("revanced_hide_shorts_channel_bar", BOOLEAN, FALSE),
|
||||
HIDE_SHORTS_NAVIGATION_BAR("revanced_hide_shorts_navigation_bar", BOOLEAN, TRUE, true),
|
||||
HIDE_SHORTS("revanced_hide_shorts", BOOLEAN, FALSE, true),
|
||||
DISABLE_SUGGESTED_VIDEO_END_SCREEN("revanced_disable_suggested_video_end_screen", BOOLEAN, TRUE),
|
||||
ENABLE_OLD_SEEKBAR_THUMBNAILS("revanced_enable_old_seekbar_thumbnails", BOOLEAN, TRUE),
|
||||
DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true),
|
||||
ALT_THUMBNAIL("revanced_alt_thumbnail", BOOLEAN, FALSE),
|
||||
ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2, parents(ALT_THUMBNAIL)),
|
||||
ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE, parents(ALT_THUMBNAIL)),
|
||||
|
||||
//Player flyout menu items
|
||||
// Player flyout menu items
|
||||
HIDE_CAPTIONS_MENU("revanced_hide_player_flyout_captions", BOOLEAN, FALSE),
|
||||
HIDE_ADDITIONAL_SETTINGS_MENU("revanced_hide_player_flyout_additional_settings", BOOLEAN, FALSE),
|
||||
HIDE_LOOP_VIDEO_MENU("revanced_hide_player_flyout_loop_video", BOOLEAN, FALSE),
|
||||
@ -172,17 +182,22 @@ public enum SettingsEnum {
|
||||
EXTERNAL_BROWSER("revanced_external_browser", BOOLEAN, TRUE, true),
|
||||
AUTO_REPEAT("revanced_auto_repeat", BOOLEAN, FALSE),
|
||||
SEEKBAR_TAPPING("revanced_seekbar_tapping", BOOLEAN, TRUE),
|
||||
SLIDE_TO_SEEK("revanced_slide_to_seek", BOOLEAN, FALSE),
|
||||
@Deprecated DISABLE_FINE_SCRUBBING_GESTURE("revanced_disable_fine_scrubbing_gesture", BOOLEAN, TRUE),
|
||||
DISABLE_PRECISE_SEEKING_GESTURE("revanced_disable_precise_seeking_gesture", BOOLEAN, TRUE),
|
||||
DISABLE_FINE_SCRUBBING_GESTURE("revanced_disable_fine_scrubbing_gesture", BOOLEAN, TRUE),
|
||||
SPOOF_SIGNATURE("revanced_spoof_signature_verification_enabled", BOOLEAN, TRUE, true,
|
||||
"revanced_spoof_signature_verification_enabled_user_dialog_message"),
|
||||
SPOOF_SIGNATURE_IN_FEED("revanced_spoof_signature_in_feed_enabled", BOOLEAN, FALSE, false,
|
||||
parents(SPOOF_SIGNATURE)),
|
||||
SPOOF_STORYBOARD_RENDERER("revanced_spoof_storyboard", BOOLEAN, TRUE, true,
|
||||
parents(SPOOF_SIGNATURE)),
|
||||
|
||||
SPOOF_DEVICE_DIMENSIONS("revanced_spoof_device_dimensions", BOOLEAN, FALSE, true),
|
||||
BYPASS_URL_REDIRECTS("revanced_bypass_url_redirects", BOOLEAN, TRUE),
|
||||
ANNOUNCEMENTS("revanced_announcements", BOOLEAN, TRUE),
|
||||
ANNOUNCEMENT_CONSUMER("revanced_announcement_consumer", STRING, ""),
|
||||
ANNOUNCEMENT_LAST_HASH("revanced_announcement_last_hash", STRING, ""),
|
||||
REMOVE_TRACKING_QUERY_PARAMETER("revanced_remove_tracking_query_parameter", BOOLEAN, TRUE),
|
||||
|
||||
// Swipe controls
|
||||
SWIPE_BRIGHTNESS("revanced_swipe_brightness", BOOLEAN, TRUE),
|
||||
@ -383,6 +398,8 @@ public enum SettingsEnum {
|
||||
|
||||
migrateOldSettingToNew(HIDE_VIDEO_WATERMARK, HIDE_VIDEO_CHANNEL_WATERMARK);
|
||||
migrateOldSettingToNew(DISABLE_FINE_SCRUBBING_GESTURE, DISABLE_PRECISE_SEEKING_GESTURE);
|
||||
migrateOldSettingToNew(SHOW_OLD_VIDEO_QUALITY_MENU, RESTORE_OLD_VIDEO_QUALITY_MENU);
|
||||
migrateOldSettingToNew(ENABLE_OLD_SEEKBAR_THUMBNAILS, RESTORE_OLD_SEEKBAR_THUMBNAILS);
|
||||
|
||||
// Do _not_ delete this SB private user id migration property until sometime in 2024.
|
||||
// This is the only setting that cannot be reconfigured if lost,
|
||||
|
@ -13,6 +13,7 @@ import android.preference.PreferenceScreen;
|
||||
import android.preference.SwitchPreference;
|
||||
|
||||
import app.revanced.integrations.patches.ReturnYouTubeDislikePatch;
|
||||
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
|
||||
import app.revanced.integrations.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
@ -21,8 +22,7 @@ import app.revanced.integrations.settings.SharedPrefCategory;
|
||||
public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
|
||||
|
||||
private static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
|
||||
SettingsEnum.SPOOF_APP_VERSION.getBoolean()
|
||||
&& SettingsEnum.SPOOF_APP_VERSION_TARGET.getString().compareTo("18.33.40") <= 0;
|
||||
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("18.33.40");
|
||||
|
||||
/**
|
||||
* If dislikes are shown on Shorts.
|
||||
|
@ -92,7 +92,7 @@ public class ReVancedUtils {
|
||||
* All tasks run at max thread priority.
|
||||
*/
|
||||
private static final ThreadPoolExecutor backgroundThreadPool = new ThreadPoolExecutor(
|
||||
2, // 2 threads always ready to go
|
||||
3, // 3 threads always ready to go
|
||||
Integer.MAX_VALUE,
|
||||
10, // For any threads over the minimum, keep them alive 10 seconds after they go idle
|
||||
TimeUnit.SECONDS,
|
||||
|
@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
android.useAndroidX = true
|
||||
version = 0.121.0
|
||||
version = 0.122.0-dev.4
|
||||
|
Loading…
x
Reference in New Issue
Block a user