CREATE_BUTTON_TITLE_RES_ID_LIST = List.of(
+ Integer.toString(Utils.getResourceIdentifier("navigationbar_musicappitems_create_title", "string"))
+ );
+
+ /**
+ * The old id of the resource which contained the Create button title. Used in older versions of the app.
+ */
+ private static final int OLD_CREATE_BUTTON_TITLE_RES_ID =
+ Utils.getResourceIdentifier("bottom_navigation_bar_create_tab_title", "string");
+
+ /**
+ * Injection point. This method is called on every navigation bar item to check whether it is the Create button.
+ * If the navigation bar item is the Create button, it returns null to erase it.
+ * The method fingerprint used to patch ensures we can safely return null here.
+ */
+ public static Object returnNullIfIsCreateButton(Object navigationBarItem) {
+ if (navigationBarItem == null) {
+ return null;
+ }
+
+ String stringifiedNavigationBarItem = navigationBarItem.toString();
+ boolean isCreateButton = CREATE_BUTTON_TITLE_RES_ID_LIST.stream()
+ .anyMatch(stringifiedNavigationBarItem::contains);
+
+ if (isCreateButton) {
+ return null;
+ }
+
+ return navigationBarItem;
+ }
+
+ /**
+ * Injection point. Called in older versions of the app. Returns whether the old navigation bar item is the old
+ * Create button.
+ */
+ public static boolean isOldCreateButton(int oldNavigationBarItemTitleResId) {
+ return oldNavigationBarItemTitleResId == OLD_CREATE_BUTTON_TITLE_RES_ID;
+ }
+}
diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/theme/CustomThemePatch.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/theme/CustomThemePatch.java
index 8949b9b2d..ef3932501 100644
--- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/theme/CustomThemePatch.java
+++ b/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/theme/CustomThemePatch.java
@@ -8,15 +8,54 @@ import app.revanced.extension.shared.Utils;
@SuppressWarnings("unused")
public final class CustomThemePatch {
+ private static final int BACKGROUND_COLOR = getColorFromString("@color/gray_7");
+ private static final int BACKGROUND_COLOR_SECONDARY = getColorFromString("@color/gray_15");
+ private static final int ACCENT_COLOR = getColorFromString("@color/spotify_green_157");
+ private static final int ACCENT_PRESSED_COLOR =
+ getColorFromString("@color/dark_brightaccent_background_press");
+
/**
- * Injection point.
+ * Returns an int representation of the color resource or hex code.
*/
- public static long getThemeColor(String colorString) {
+ private static int getColorFromString(String colorString) {
try {
return Utils.getColorFromString(colorString);
} catch (Exception ex) {
- Logger.printException(() -> "Invalid custom color: " + colorString, ex);
+ Logger.printException(() -> "Invalid color string: " + colorString, ex);
return Color.BLACK;
}
}
+
+ /**
+ * Injection point. Returns an int representation of the replaced color from the original color.
+ */
+ public static int replaceColor(int originalColor) {
+ switch (originalColor) {
+ // Playlist background color.
+ case 0xFF121212:
+ return BACKGROUND_COLOR;
+
+ // Share menu background color.
+ case 0xFF1F1F1F:
+ // Home category pills background color.
+ case 0xFF333333:
+ // Settings header background color.
+ case 0xFF282828:
+ // Spotify Connect device list background color.
+ case 0xFF2A2A2A:
+ return BACKGROUND_COLOR_SECONDARY;
+
+ // Some Lottie animations have a color that's slightly off due to rounding errors.
+ case 0xFF1ED760: case 0xFF1ED75F:
+ // Intermediate color used in some animations, same rounding issue.
+ case 0xFF1DB954: case 0xFF1CB854:
+ return ACCENT_COLOR;
+
+ case 0xFF1ABC54:
+ return ACCENT_PRESSED_COLOR;
+
+ default:
+ return originalColor;
+ }
+ }
}
diff --git a/extensions/spotify/stub/build.gradle.kts b/extensions/spotify/stub/build.gradle.kts
index a8da923ed..61a9e204a 100644
--- a/extensions/spotify/stub/build.gradle.kts
+++ b/extensions/spotify/stub/build.gradle.kts
@@ -7,11 +7,11 @@ android {
compileSdk = 34
defaultConfig {
- minSdk = 26
+ minSdk = 24
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
}
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java
index 0177c9cbc..d6594f20d 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java
@@ -105,6 +105,14 @@ public class ThemeHelper {
return isDarkTheme() ? getLightThemeColor() : getDarkThemeColor();
}
+ public static int getDialogBackgroundColor() {
+ final String colorName = isDarkTheme()
+ ? "yt_black1"
+ : "yt_white1";
+
+ return Utils.getColorFromString(colorName);
+ }
+
public static int getToolbarBackgroundColor() {
final String colorName = isDarkTheme()
? "yt_black3"
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java
index c670a79de..a4dd50fcc 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java
@@ -686,7 +686,7 @@ public final class AlternativeThumbnailsPatch {
? "" : fullUrl.substring(imageExtensionEndIndex);
}
- /** @noinspection SameParameterValue */
+ @SuppressWarnings("SameParameterValue")
String createStillsUrl(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) {
// Images could be upgraded to webp if they are not already, but this fails quite often,
// especially for new videos uploaded in the last hour.
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java
index 2db744ae6..bb351b66b 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch.java
@@ -152,11 +152,13 @@ public class ReturnYouTubeDislikePatch {
return original; // No need to check for Shorts in the context.
}
- if (conversionContextString.contains("|shorts_dislike_button.eml")) {
+ if (Utils.containsAny(conversionContextString,
+ "|shorts_dislike_button.eml", "|reel_dislike_button.eml")) {
return getShortsSpan(original, true);
}
- if (conversionContextString.contains("|shorts_like_button.eml")) {
+ if (Utils.containsAny(conversionContextString,
+ "|shorts_like_button.eml", "|reel_like_button.eml")) {
if (!Utils.containsNumber(original)) {
Logger.printDebug(() -> "Replacing hidden likes count");
return getShortsSpan(original, false);
@@ -361,6 +363,11 @@ public class ReturnYouTubeDislikePatch {
if (videoId.equals(lastPrefetchedVideoId)) {
return;
}
+ if (!Utils.isNetworkConnected()) {
+ Logger.printDebug(() -> "Cannot pre-fetch RYD, network is not connected");
+ lastPrefetchedVideoId = null;
+ return;
+ }
final boolean videoIdIsShort = VideoInformation.lastPlayerResponseIsShort();
// Shorts shelf in home and subscription feed causes player response hook to be called,
@@ -415,6 +422,12 @@ public class ReturnYouTubeDislikePatch {
}
Logger.printDebug(() -> "New video id: " + videoId + " playerType: " + currentPlayerType);
+ if (!Utils.isNetworkConnected()) {
+ Logger.printDebug(() -> "Cannot fetch RYD, network is not connected");
+ currentVideoData = null;
+ 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.
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
index 6df9a9095..99d8a5b6a 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
@@ -354,4 +354,23 @@ public final class VideoInformation {
return videoTime >= videoLength && videoLength > 0;
}
+ /**
+ * Overrides the current playback speed.
+ * Rest of the implementation added by patch.
+ */
+ public static void overridePlaybackSpeed(float speedOverride) {
+ Logger.printDebug(() -> "Overriding playback speed to: " + speedOverride);
+ }
+
+ /**
+ * Injection point.
+ *
+ * @param newlyLoadedPlaybackSpeed The current playback speed.
+ */
+ public static void setPlaybackSpeed(float newlyLoadedPlaybackSpeed) {
+ if (playbackSpeed != newlyLoadedPlaybackSpeed) {
+ Logger.printDebug(() -> "Video speed changed: " + newlyLoadedPlaybackSpeed);
+ playbackSpeed = newlyLoadedPlaybackSpeed;
+ }
+ }
}
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java
index d630ee9ed..531578a8b 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch.java
@@ -10,18 +10,11 @@ import app.revanced.extension.youtube.settings.Settings;
*/
public final class PlaybackSpeedMenuFilterPatch extends Filter {
- /**
- * Old litho based speed selection menu.
- */
- public static volatile boolean isOldPlaybackSpeedMenuVisible;
-
/**
* 0.05x speed selection menu.
*/
public static volatile boolean isPlaybackRateSelectorMenuVisible;
- private final StringFilterGroup oldPlaybackMenuGroup;
-
public PlaybackSpeedMenuFilterPatch() {
// 0.05x litho speed menu.
var playbackRateSelectorGroup = new StringFilterGroup(
@@ -29,22 +22,13 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter {
"playback_rate_selector_menu_sheet.eml-js"
);
- // Old litho based speed menu.
- oldPlaybackMenuGroup = new StringFilterGroup(
- Settings.CUSTOM_SPEED_MENU,
- "playback_speed_sheet_content.eml-js");
-
- addPathCallbacks(playbackRateSelectorGroup, oldPlaybackMenuGroup);
+ addPathCallbacks(playbackRateSelectorGroup);
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
- if (matchedGroup == oldPlaybackMenuGroup) {
- isOldPlaybackSpeedMenuVisible = true;
- } else {
- isPlaybackRateSelectorMenuVisible = true;
- }
+ isPlaybackRateSelectorMenuVisible = true;
return false;
}
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java
index ef1cd5bb5..752cca7cb 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java
@@ -143,12 +143,14 @@ public final class ShortsFilter extends Filter {
StringFilterGroup likeButton = new StringFilterGroup(
Settings.HIDE_SHORTS_LIKE_BUTTON,
- "shorts_like_button.eml"
+ "shorts_like_button.eml",
+ "reel_like_button.eml"
);
StringFilterGroup dislikeButton = new StringFilterGroup(
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
- "shorts_dislike_button.eml"
+ "shorts_dislike_button.eml",
+ "reel_dislike_button.eml"
);
joinButton = new StringFilterGroup(
@@ -168,12 +170,13 @@ public final class ShortsFilter extends Filter {
shortsActionBar = new StringFilterGroup(
null,
- "shorts_action_bar.eml"
+ "shorts_action_bar.eml",
+ "reel_action_bar.eml"
);
actionButton = new StringFilterGroup(
null,
- // Can be simply 'button.eml' or 'shorts_video_action_button.eml'
+ // Can be simply 'button.eml', 'shorts_video_action_button.eml' or 'reel_action_button.eml'
"button.eml"
);
@@ -195,15 +198,18 @@ public final class ShortsFilter extends Filter {
videoActionButtonGroupList.addAll(
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_COMMENTS_BUTTON,
- "reel_comment_button"
+ "reel_comment_button",
+ "youtube_shorts_comment_outline"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_SHARE_BUTTON,
- "reel_share_button"
+ "reel_share_button",
+ "youtube_shorts_share_outline"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_REMIX_BUTTON,
- "reel_remix_button"
+ "reel_remix_button",
+ "youtube_shorts_remix_outline"
)
);
@@ -211,6 +217,12 @@ public final class ShortsFilter extends Filter {
// Suggested actions.
//
suggestedActionsGroupList.addAll(
+ new ByteArrayFilterGroup(
+ Settings.HIDE_SHORTS_COMMENT_PANEL,
+ // Preview comment that can popup while a Short is playing.
+ // Uses no bundled icons, and instead the users profile photo is shown.
+ "shorts-comments-panel"
+ ),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_SHOP_BUTTON,
"yt_outline_bag_"
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java
index 9b6224106..4c509bccb 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java
@@ -1,24 +1,57 @@
package app.revanced.extension.youtube.patches.playback.speed;
import static app.revanced.extension.shared.StringRef.str;
+import static app.revanced.extension.shared.Utils.dipToPixels;
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.icu.text.NumberFormat;
import android.support.v7.widget.RecyclerView;
+import android.view.animation.Animation;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.GridLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import java.lang.ref.WeakReference;
import java.util.Arrays;
+import java.util.function.Function;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
+import app.revanced.extension.youtube.ThemeHelper;
+import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilterPatch;
import app.revanced.extension.youtube.settings.Settings;
+import app.revanced.extension.youtube.shared.PlayerType;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
@SuppressWarnings("unused")
public class CustomPlaybackSpeedPatch {
/**
- * Maximum playback speed, exclusive value. Custom speeds must be less than this value.
+ * Maximum playback speed, inclusive. Custom speeds must be this or less.
*
* Going over 8x does not increase the actual playback speed any higher,
* and the UI selector starts flickering and acting weird.
@@ -26,6 +59,11 @@ public class CustomPlaybackSpeedPatch {
*/
public static final float PLAYBACK_SPEED_MAXIMUM = 8;
+ /**
+ * Scale used to convert user speed to {@link android.widget.ProgressBar#setProgress(int)}.
+ */
+ private static final float PROGRESS_BAR_VALUE_SCALE = 100;
+
/**
* Tap and hold speed.
*/
@@ -34,16 +72,28 @@ public class CustomPlaybackSpeedPatch {
/**
* Custom playback speeds.
*/
- public static float[] customPlaybackSpeeds;
+ public static final float[] customPlaybackSpeeds;
/**
- * The last time the old playback menu was forcefully called.
+ * Formats speeds to UI strings.
*/
- private static long lastTimeOldPlaybackMenuInvoked;
+ private static final NumberFormat speedFormatter = NumberFormat.getNumberInstance();
+
+ /**
+ * Weak reference to the currently open dialog.
+ */
+ private static WeakReference