diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java index bb19d2497b..5d6f6ce54a 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java @@ -11,6 +11,7 @@ import app.revanced.extension.music.settings.preference.MusicPreferenceFragment; import app.revanced.extension.music.settings.search.MusicSearchViewController; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseActivityHook; @@ -46,15 +47,7 @@ protected void customizeActivityTheme(Activity activity) { // Override the default YouTube Music theme to increase start padding of list items. // Custom style located in resources/music/values/style.xml activity.setTheme(Utils.getResourceIdentifierOrThrow( - "Theme.ReVanced.YouTubeMusic.Settings", "style")); - } - - /** - * Returns the resource ID for the YouTube Music settings layout. - */ - @Override - protected int getContentViewResourceId() { - return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR; + ResourceType.STYLE, "Theme.ReVanced.YouTubeMusic.Settings")); } /** diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/ResourceType.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ResourceType.java new file mode 100644 index 0000000000..48032017a4 --- /dev/null +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ResourceType.java @@ -0,0 +1,57 @@ +package app.revanced.extension.shared; + +import java.util.HashMap; +import java.util.Map; + +public enum ResourceType { + ANIM("anim"), + ANIMATOR("animator"), + ARRAY("array"), + ATTR("attr"), + BOOL("bool"), + COLOR("color"), + DIMEN("dimen"), + DRAWABLE("drawable"), + FONT("font"), + FRACTION("fraction"), + ID("id"), + INTEGER("integer"), + INTERPOLATOR("interpolator"), + LAYOUT("layout"), + MENU("menu"), + MIPMAP("mipmap"), + NAVIGATION("navigation"), + PLURALS("plurals"), + RAW("raw"), + STRING("string"), + STYLE("style"), + STYLEABLE("styleable"), + TRANSITION("transition"), + VALUES("values"), + XML("xml"); + + private static final Map VALUE_MAP; + + static { + ResourceType[] values = values(); + VALUE_MAP = new HashMap<>(2 * values.length); + + for (ResourceType type : values) { + VALUE_MAP.put(type.value, type); + } + } + + public final String value; + + public static ResourceType fromValue(String value) { + ResourceType type = VALUE_MAP.get(value); + if (type == null) { + throw new IllegalArgumentException("Unknown resource type: " + value); + } + return type; + } + + ResourceType(String value) { + this.value = value; + } +} diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java index 5d6bb492d5..e1d82f4e7a 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java @@ -32,7 +32,11 @@ import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.Toast; +import android.widget.Toolbar; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; @@ -43,8 +47,10 @@ import java.text.Normalizer; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.Future; @@ -76,6 +82,8 @@ public class Utils { @Nullable private static Boolean isDarkModeEnabled; + private static boolean appIsUsingBoldIcons; + // Cached Collator instance with its locale. @Nullable private static Locale cachedCollatorLocale; @@ -148,12 +156,12 @@ public static String getApplicationName() { /** * Hide a view by setting its layout height and width to 1dp. * - * @param condition The setting to check for hiding the view. + * @param setting The setting to check for hiding the view. * @param view The view to hide. */ - public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View view) { - if (hideViewBy0dpUnderCondition(condition.get(), view)) { - Logger.printDebug(() -> "View hidden by setting: " + condition); + public static void hideViewBy0dpUnderCondition(BooleanSetting setting, View view) { + if (hideViewBy0dpUnderCondition(setting.get(), view)) { + Logger.printDebug(() -> "View hidden by setting: " + setting); } } @@ -165,22 +173,47 @@ public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View vi */ public static boolean hideViewBy0dpUnderCondition(boolean condition, View view) { if (condition) { - hideViewByLayoutParams(view); + hideViewBy0dp(view); return true; } return false; } + /** + * Hide a view by setting its layout params to 0x0 + * @param view The view to hide. + */ + public static void hideViewBy0dp(View view) { + if (view instanceof LinearLayout) { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, 0); + view.setLayoutParams(layoutParams); + } else if (view instanceof FrameLayout) { + FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(0, 0); + view.setLayoutParams(layoutParams2); + } else if (view instanceof RelativeLayout) { + RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(0, 0); + view.setLayoutParams(layoutParams3); + } else if (view instanceof Toolbar) { + Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(0, 0); + view.setLayoutParams(layoutParams4); + } else { + ViewGroup.LayoutParams params = view.getLayoutParams(); + params.width = 0; + params.height = 0; + view.setLayoutParams(params); + } + } + /** * Hide a view by setting its visibility to GONE. * - * @param condition The setting to check for hiding the view. + * @param setting The setting to check for hiding the view. * @param view The view to hide. */ - public static void hideViewUnderCondition(BooleanSetting condition, View view) { - if (hideViewUnderCondition(condition.get(), view)) { - Logger.printDebug(() -> "View hidden by setting: " + condition); + public static void hideViewUnderCondition(BooleanSetting setting, View view) { + if (hideViewUnderCondition(setting.get(), view)) { + Logger.printDebug(() -> "View hidden by setting: " + setting); } } @@ -199,14 +232,14 @@ public static boolean hideViewUnderCondition(boolean condition, View view) { return false; } - public static void hideViewByRemovingFromParentUnderCondition(BooleanSetting condition, View view) { - if (hideViewByRemovingFromParentUnderCondition(condition.get(), view)) { - Logger.printDebug(() -> "View hidden by setting: " + condition); + public static void hideViewByRemovingFromParentUnderCondition(BooleanSetting setting, View view) { + if (hideViewByRemovingFromParentUnderCondition(setting.get(), view)) { + Logger.printDebug(() -> "View hidden by setting: " + setting); } } - public static boolean hideViewByRemovingFromParentUnderCondition(boolean setting, View view) { - if (setting) { + public static boolean hideViewByRemovingFromParentUnderCondition(boolean condition, View view) { + if (condition) { ViewParent parent = view.getParent(); if (parent instanceof ViewGroup parentGroup) { parentGroup.removeView(view); @@ -278,12 +311,13 @@ public static int indexOfFirstFound(String value, String... targets) { * @return zero, if the resource is not found. */ @SuppressLint("DiscouragedApi") - public static int getResourceIdentifier(Context context, String resourceIdentifierName, @Nullable String type) { - return context.getResources().getIdentifier(resourceIdentifierName, type, context.getPackageName()); + public static int getResourceIdentifier(Context context, @Nullable ResourceType type, String resourceIdentifierName) { + return context.getResources().getIdentifier(resourceIdentifierName, + type == null ? null : type.value, context.getPackageName()); } - public static int getResourceIdentifierOrThrow(Context context, String resourceIdentifierName, @Nullable String type) { - final int resourceId = getResourceIdentifier(context, resourceIdentifierName, type); + public static int getResourceIdentifierOrThrow(Context context, @Nullable ResourceType type, String resourceIdentifierName) { + final int resourceId = getResourceIdentifier(context, type, resourceIdentifierName); if (resourceId == 0) { throw new Resources.NotFoundException("No resource id exists with name: " + resourceIdentifierName + " type: " + type); @@ -293,48 +327,44 @@ public static int getResourceIdentifierOrThrow(Context context, String resourceI /** * @return zero, if the resource is not found. - * @see #getResourceIdentifierOrThrow(String, String) + * @see #getResourceIdentifierOrThrow(ResourceType, String) */ - public static int getResourceIdentifier(String resourceIdentifierName, @Nullable String type) { - return getResourceIdentifier(getContext(), resourceIdentifierName, type); + public static int getResourceIdentifier(@Nullable ResourceType type, String resourceIdentifierName) { + return getResourceIdentifier(getContext(), type, resourceIdentifierName); } /** - * @return The resource identifier, or throws an exception if not found. + * @return zero, if the resource is not found. + * @see #getResourceIdentifier(ResourceType, String) */ - public static int getResourceIdentifierOrThrow(String resourceIdentifierName, @Nullable String type) { - final int resourceId = getResourceIdentifier(getContext(), resourceIdentifierName, type); - if (resourceId == 0) { - throw new Resources.NotFoundException("No resource id exists with name: " + resourceIdentifierName - + " type: " + type); - } - return resourceId; + public static int getResourceIdentifierOrThrow(@Nullable ResourceType type, String resourceIdentifierName) { + return getResourceIdentifierOrThrow(getContext(), type, resourceIdentifierName); } public static int getResourceInteger(String resourceIdentifierName) throws Resources.NotFoundException { - return getContext().getResources().getInteger(getResourceIdentifierOrThrow(resourceIdentifierName, "integer")); + return getContext().getResources().getInteger(getResourceIdentifierOrThrow(ResourceType.INTEGER, resourceIdentifierName)); } public static Animation getResourceAnimation(String resourceIdentifierName) throws Resources.NotFoundException { - return AnimationUtils.loadAnimation(getContext(), getResourceIdentifierOrThrow(resourceIdentifierName, "anim")); + return AnimationUtils.loadAnimation(getContext(), getResourceIdentifierOrThrow(ResourceType.ANIM, resourceIdentifierName)); } @ColorInt public static int getResourceColor(String resourceIdentifierName) throws Resources.NotFoundException { //noinspection deprecation - return getContext().getResources().getColor(getResourceIdentifierOrThrow(resourceIdentifierName, "color")); + return getContext().getResources().getColor(getResourceIdentifierOrThrow(ResourceType.COLOR, resourceIdentifierName)); } public static int getResourceDimensionPixelSize(String resourceIdentifierName) throws Resources.NotFoundException { - return getContext().getResources().getDimensionPixelSize(getResourceIdentifierOrThrow(resourceIdentifierName, "dimen")); + return getContext().getResources().getDimensionPixelSize(getResourceIdentifierOrThrow(ResourceType.DIMEN, resourceIdentifierName)); } public static float getResourceDimension(String resourceIdentifierName) throws Resources.NotFoundException { - return getContext().getResources().getDimension(getResourceIdentifierOrThrow(resourceIdentifierName, "dimen")); + return getContext().getResources().getDimension(getResourceIdentifierOrThrow(ResourceType.DIMEN, resourceIdentifierName)); } public static String[] getResourceStringArray(String resourceIdentifierName) throws Resources.NotFoundException { - return getContext().getResources().getStringArray(getResourceIdentifierOrThrow(resourceIdentifierName, "array")); + return getContext().getResources().getStringArray(getResourceIdentifierOrThrow(ResourceType.ARRAY, resourceIdentifierName)); } public interface MatchFilter { @@ -345,7 +375,7 @@ public interface MatchFilter { * Includes sub children. */ public static R getChildViewByResourceName(View view, String str) { - var child = view.findViewById(Utils.getResourceIdentifierOrThrow(str, "id")); + var child = view.findViewById(Utils.getResourceIdentifierOrThrow(ResourceType.ID, str)); //noinspection unchecked return (R) child; } @@ -802,6 +832,21 @@ public static void setDialogWindowParameters(Window window, int gravity, int yOf window.setBackgroundDrawable(null); // Remove default dialog background } + /** + * @return If the unpatched app is currently using bold icons. + */ + public static boolean appIsUsingBoldIcons() { + return appIsUsingBoldIcons; + } + + /** + * Controls if ReVanced bold icons are shown in various places. + * @param boldIcons If the app is currently using bold icons. + */ + public static void setAppIsUsingBoldIcons(boolean boldIcons) { + appIsUsingBoldIcons = boldIcons; + } + /** * Sets the theme light color used by the app. */ @@ -1163,4 +1208,18 @@ public static int clamp(int value, int lower, int upper) { public static float clamp(float value, float lower, float upper) { return Math.max(lower, Math.min(value, upper)); } + + /** + * @param maxSize The maximum number of elements to keep in the map. + * @return A {@link LinkedHashMap} that automatically evicts the oldest entry + * when the size exceeds {@code maxSize}. + */ + public static Map createSizeRestrictedMap(int maxSize) { + return new LinkedHashMap<>(2 * maxSize) { + @Override + protected boolean removeEldestEntry(Entry eldest) { + return size() > maxSize; + } + }; + } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/checks/Check.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/checks/Check.java index ccb7c9262e..53db12c4d7 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/checks/Check.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/checks/Check.java @@ -23,6 +23,7 @@ import java.util.Collection; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.ui.CustomDialog; @@ -128,7 +129,7 @@ static void issueWarning(Activity activity, Collection failedChecks) { // Add icon to the dialog. ImageView iconView = new ImageView(activity); iconView.setImageResource(Utils.getResourceIdentifierOrThrow( - "revanced_ic_dialog_alert", "drawable")); + ResourceType.DRAWABLE, "revanced_ic_dialog_alert")); iconView.setColorFilter(Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN); iconView.setPadding(0, 0, 0, 0); LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams( diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/fixes/redgifs/BaseFixRedgifsApiPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/fixes/redgifs/BaseFixRedgifsApiPatch.java index b6fa2caa0c..00ee6def3b 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/fixes/redgifs/BaseFixRedgifsApiPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/fixes/redgifs/BaseFixRedgifsApiPatch.java @@ -15,7 +15,6 @@ import okhttp3.Response; import okhttp3.ResponseBody; - public abstract class BaseFixRedgifsApiPatch implements Interceptor { protected static BaseFixRedgifsApiPatch INSTANCE; public abstract String getDefaultUserAgent(); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java index 9908c0be75..2b81178eac 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java @@ -13,6 +13,7 @@ import app.revanced.extension.shared.GmsCoreSupport; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseSettings; @@ -65,7 +66,7 @@ private String packageAndNameIndexToClassAlias(String packageName, int appIndex) iconName += "_custom"; } - notificationSmallIcon = Utils.getResourceIdentifier(iconName, "drawable"); + notificationSmallIcon = Utils.getResourceIdentifier(ResourceType.DRAWABLE, iconName); if (notificationSmallIcon == 0) { Logger.printException(() -> "Could not load notification small icon"); } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java index fb068d8ede..0076e6d762 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java @@ -13,6 +13,7 @@ import android.widget.Toolbar; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment; import app.revanced.extension.shared.ui.Dim; @@ -25,13 +26,13 @@ public abstract class BaseActivityHook extends Activity { private static final int ID_REVANCED_SETTINGS_FRAGMENTS = - getResourceIdentifierOrThrow("revanced_settings_fragments", "id"); + getResourceIdentifierOrThrow(ResourceType.ID, "revanced_settings_fragments"); private static final int ID_REVANCED_TOOLBAR_PARENT = - getResourceIdentifierOrThrow("revanced_toolbar_parent", "id"); + getResourceIdentifierOrThrow(ResourceType.ID, "revanced_toolbar_parent"); public static final int LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR = - getResourceIdentifierOrThrow("revanced_settings_with_toolbar", "layout"); + getResourceIdentifierOrThrow(ResourceType.LAYOUT, "revanced_settings_with_toolbar"); private static final int STRING_REVANCED_SETTINGS_TITLE = - getResourceIdentifierOrThrow("revanced_settings_title", "string"); + getResourceIdentifierOrThrow(ResourceType.STRING, "revanced_settings_title"); /** * Layout parameters for the toolbar, extracted from the dummy toolbar. @@ -124,14 +125,16 @@ protected void createToolbar(Activity activity, PreferenceFragment fragment) { } /** - * Customizes the activity's theme. + * Returns the resource ID for the content view layout. */ - protected abstract void customizeActivityTheme(Activity activity); + protected int getContentViewResourceId() { + return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR; + } /** - * Returns the resource ID for the content view layout. + * Customizes the activity's theme. */ - protected abstract int getContentViewResourceId(); + protected abstract void customizeActivityTheme(Activity activity); /** * Returns the background color for the toolbar. diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index e9dc280a8d..b13c74c839 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -5,6 +5,8 @@ import static app.revanced.extension.shared.patches.CustomBrandingPatch.BrandingTheme; import static app.revanced.extension.shared.settings.Setting.parent; +import app.revanced.extension.shared.Logger; + /** * Settings shared across multiple apps. *

@@ -24,10 +26,19 @@ public class BaseSettings { * Use the icons declared in the preferences created during patching. If no icons or styles are declared then this setting does nothing. */ public static final BooleanSetting SHOW_MENU_ICONS = new BooleanSetting("revanced_show_menu_icons", TRUE, true); + /** + * Do not use this setting directly. Instead use {@link app.revanced.extension.shared.Utils#appIsUsingBoldIcons()} + */ + public static final BooleanSetting SETTINGS_DISABLE_BOLD_ICONS = new BooleanSetting("revanced_settings_disable_bold_icons", FALSE, true); public static final BooleanSetting SETTINGS_SEARCH_HISTORY = new BooleanSetting("revanced_settings_search_history", TRUE, true); public static final StringSetting SETTINGS_SEARCH_ENTRIES = new StringSetting("revanced_settings_search_entries", ""); + /** + * The first time the app was launched with no previous app data (either a clean install, or after wiping app data). + */ + public static final LongSetting FIRST_TIME_APP_LAUNCHED = new LongSetting("revanced_last_time_app_was_launched", -1L, false, false); + // // Settings shared by YouTube and YouTube Music. // @@ -44,4 +55,13 @@ public class BaseSettings { public static final IntegerSetting CUSTOM_BRANDING_NAME = new IntegerSetting("revanced_custom_branding_name", 1, true); public static final StringSetting DISABLED_FEATURE_FLAGS = new StringSetting("revanced_disabled_feature_flags", "", true, parent(DEBUG)); + + static { + final long now = System.currentTimeMillis(); + + if (FIRST_TIME_APP_LAUNCHED.get() < 0) { + Logger.printInfo(() -> "First launch of installation with no prior app data"); + FIRST_TIME_APP_LAUNCHED.save(now); + } + } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/AbstractPreferenceFragment.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/AbstractPreferenceFragment.java index 6e0e8957b9..70ae3a5ab2 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/AbstractPreferenceFragment.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/AbstractPreferenceFragment.java @@ -23,6 +23,7 @@ import java.util.Objects; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BooleanSetting; @@ -103,10 +104,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment { * so all app specific {@link Setting} instances are loaded before this method returns. */ protected void initialize() { - String preferenceResourceName = BaseSettings.SHOW_MENU_ICONS.get() - ? "revanced_prefs_icons" - : "revanced_prefs"; - final var identifier = Utils.getResourceIdentifier(preferenceResourceName, "xml"); + String preferenceResourceName; + if (BaseSettings.SHOW_MENU_ICONS.get()) { + preferenceResourceName = Utils.appIsUsingBoldIcons() + ? "revanced_prefs_icons_bold" + : "revanced_prefs_icons"; + } else { + preferenceResourceName = "revanced_prefs"; + } + + final var identifier = Utils.getResourceIdentifier(ResourceType.XML, preferenceResourceName); if (identifier == 0) return; addPreferencesFromResource(identifier); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java index e3d6abee25..c9fc7b6da9 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java @@ -31,6 +31,7 @@ import java.util.regex.Pattern; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.StringSetting; @@ -81,13 +82,13 @@ public class ColorPickerPreference extends EditTextPreference { private boolean opacitySliderEnabled = false; public static final int ID_REVANCED_COLOR_PICKER_VIEW = - getResourceIdentifierOrThrow("revanced_color_picker_view", "id"); + getResourceIdentifierOrThrow(ResourceType.ID, "revanced_color_picker_view"); public static final int ID_PREFERENCE_COLOR_DOT = - getResourceIdentifierOrThrow("preference_color_dot", "id"); + getResourceIdentifierOrThrow(ResourceType.ID, "preference_color_dot"); public static final int LAYOUT_REVANCED_COLOR_DOT_WIDGET = - getResourceIdentifierOrThrow("revanced_color_dot_widget", "layout"); + getResourceIdentifierOrThrow(ResourceType.LAYOUT, "revanced_color_dot_widget"); public static final int LAYOUT_REVANCED_COLOR_PICKER = - getResourceIdentifierOrThrow("revanced_color_picker", "layout"); + getResourceIdentifierOrThrow(ResourceType.LAYOUT, "revanced_color_picker"); /** * Removes non valid hex characters, converts to all uppercase, diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/CustomDialogListPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/CustomDialogListPreference.java index ff728838b7..48c50c1f33 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/CustomDialogListPreference.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/CustomDialogListPreference.java @@ -20,6 +20,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.ui.CustomDialog; @@ -30,14 +31,18 @@ @SuppressWarnings({"unused", "deprecation"}) public class CustomDialogListPreference extends ListPreference { - public static final int ID_REVANCED_CHECK_ICON = - getResourceIdentifierOrThrow("revanced_check_icon", "id"); - public static final int ID_REVANCED_CHECK_ICON_PLACEHOLDER = - getResourceIdentifierOrThrow("revanced_check_icon_placeholder", "id"); - public static final int ID_REVANCED_ITEM_TEXT = - getResourceIdentifierOrThrow("revanced_item_text", "id"); - public static final int LAYOUT_REVANCED_CUSTOM_LIST_ITEM_CHECKED = - getResourceIdentifierOrThrow("revanced_custom_list_item_checked", "layout"); + public static final int ID_REVANCED_CHECK_ICON = getResourceIdentifierOrThrow( + ResourceType.ID, "revanced_check_icon"); + public static final int ID_REVANCED_CHECK_ICON_PLACEHOLDER = getResourceIdentifierOrThrow( + ResourceType.ID, "revanced_check_icon_placeholder"); + public static final int ID_REVANCED_ITEM_TEXT = getResourceIdentifierOrThrow( + ResourceType.ID, "revanced_item_text"); + public static final int LAYOUT_REVANCED_CUSTOM_LIST_ITEM_CHECKED = getResourceIdentifierOrThrow( + ResourceType.LAYOUT, "revanced_custom_list_item_checked"); + public static final int DRAWABLE_CHECKMARK = getResourceIdentifierOrThrow( + ResourceType.DRAWABLE, "revanced_settings_custom_checkmark"); + public static final int DRAWABLE_CHECKMARK_BOLD = getResourceIdentifierOrThrow( + ResourceType.DRAWABLE, "revanced_settings_custom_checkmark_bold"); private String staticSummary = null; private CharSequence[] highlightedEntriesForDialog = null; @@ -125,9 +130,13 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(getContext()); view = inflater.inflate(layoutResourceId, parent, false); holder = new SubViewDataContainer(); - holder.checkIcon = view.findViewById(ID_REVANCED_CHECK_ICON); holder.placeholder = view.findViewById(ID_REVANCED_CHECK_ICON_PLACEHOLDER); holder.itemText = view.findViewById(ID_REVANCED_ITEM_TEXT); + holder.checkIcon = view.findViewById(ID_REVANCED_CHECK_ICON); + holder.checkIcon.setImageResource(Utils.appIsUsingBoldIcons() + ? DRAWABLE_CHECKMARK_BOLD + : DRAWABLE_CHECKMARK + ); view.setTag(holder); } else { holder = (SubViewDataContainer) view.getTag(); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/FeatureFlagsManagerPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/FeatureFlagsManagerPreference.java index 1ada6584ac..061f230e4f 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/FeatureFlagsManagerPreference.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/FeatureFlagsManagerPreference.java @@ -38,6 +38,7 @@ import java.util.TreeSet; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.patches.EnableDebuggingPatch; import app.revanced.extension.shared.settings.BaseSettings; @@ -52,25 +53,26 @@ public class FeatureFlagsManagerPreference extends Preference { private static final int DRAWABLE_REVANCED_SETTINGS_SELECT_ALL = - getResourceIdentifierOrThrow("revanced_settings_select_all", "drawable"); + getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_select_all"); private static final int DRAWABLE_REVANCED_SETTINGS_DESELECT_ALL = - getResourceIdentifierOrThrow("revanced_settings_deselect_all", "drawable"); + getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_deselect_all"); private static final int DRAWABLE_REVANCED_SETTINGS_COPY_ALL = - getResourceIdentifierOrThrow("revanced_settings_copy_all", "drawable"); + getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_copy_all"); private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_ONE = - getResourceIdentifierOrThrow("revanced_settings_arrow_right_one", "drawable"); + getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_right_one"); private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_DOUBLE = - getResourceIdentifierOrThrow("revanced_settings_arrow_right_double", "drawable"); + getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_right_double"); private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_ONE = - getResourceIdentifierOrThrow("revanced_settings_arrow_left_one", "drawable"); + getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_left_one"); private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_DOUBLE = - getResourceIdentifierOrThrow("revanced_settings_arrow_left_double", "drawable"); + getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_left_double"); /** * Flags to hide from the UI. */ private static final Set FLAGS_TO_IGNORE = Set.of( - 45386834L // 'You' tab settings icon. + 45386834L, // 'You' tab settings icon. + 45685201L // Bold icons. Forcing off interferes with patch changes and YT icons are broken. ); /** diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java index 5c595a97ae..cc0a642745 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java @@ -17,9 +17,11 @@ import androidx.annotation.Nullable; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseActivityHook; import app.revanced.extension.shared.ui.Dim; +import app.revanced.extension.shared.settings.BaseSettings; @SuppressWarnings({"deprecation", "NewApi"}) public class ToolbarPreferenceFragment extends AbstractPreferenceFragment { @@ -133,8 +135,10 @@ public static void setNavigationBarColor(@Nullable Window window) { */ @SuppressLint("UseCompatLoadingForDrawables") public static Drawable getBackButtonDrawable() { - final int backButtonResource = Utils.getResourceIdentifierOrThrow( - "revanced_settings_toolbar_arrow_left", "drawable"); + final int backButtonResource = Utils.getResourceIdentifierOrThrow(ResourceType.DRAWABLE, + Utils.appIsUsingBoldIcons() + ? "revanced_settings_toolbar_arrow_left_bold" + : "revanced_settings_toolbar_arrow_left"); Drawable drawable = Utils.getContext().getResources().getDrawable(backButtonResource); customizeBackButtonDrawable(drawable); return drawable; diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultItem.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultItem.java index ab1e2ee6c1..7b5830a463 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultItem.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultItem.java @@ -16,6 +16,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.preference.ColorPickerPreference; import app.revanced.extension.shared.settings.preference.CustomDialogListPreference; @@ -38,18 +39,18 @@ public enum ViewType { // Get the corresponding layout resource ID. public int getLayoutResourceId() { return switch (this) { - case REGULAR, URL_LINK -> getResourceIdentifier("revanced_preference_search_result_regular"); - case SWITCH -> getResourceIdentifier("revanced_preference_search_result_switch"); - case LIST -> getResourceIdentifier("revanced_preference_search_result_list"); - case COLOR_PICKER -> getResourceIdentifier("revanced_preference_search_result_color"); - case GROUP_HEADER -> getResourceIdentifier("revanced_preference_search_result_group_header"); - case NO_RESULTS -> getResourceIdentifier("revanced_preference_search_no_result"); + case REGULAR, URL_LINK -> getResourceIdentifier("revanced_preference_search_result_regular"); + case SWITCH -> getResourceIdentifier("revanced_preference_search_result_switch"); + case LIST -> getResourceIdentifier("revanced_preference_search_result_list"); + case COLOR_PICKER -> getResourceIdentifier("revanced_preference_search_result_color"); + case GROUP_HEADER -> getResourceIdentifier("revanced_preference_search_result_group_header"); + case NO_RESULTS -> getResourceIdentifier("revanced_preference_search_no_result"); }; } private static int getResourceIdentifier(String name) { // Placeholder for actual resource identifier retrieval. - return Utils.getResourceIdentifierOrThrow(name, "layout"); + return Utils.getResourceIdentifierOrThrow(ResourceType.LAYOUT, name); } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java index cb5e792e83..d6a8167f4e 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/search/BaseSearchResultsAdapter.java @@ -1,7 +1,6 @@ package app.revanced.extension.shared.settings.search; import static app.revanced.extension.shared.Utils.getResourceIdentifierOrThrow; -import static app.revanced.extension.shared.settings.search.BaseSearchViewController.DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON; import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; @@ -33,6 +32,7 @@ import java.util.List; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.preference.ColorPickerPreference; import app.revanced.extension.shared.settings.preference.CustomDialogListPreference; @@ -54,15 +54,15 @@ public abstract class BaseSearchResultsAdapter extends ArrayAdapter searchHistory; private final Activity activity; @@ -97,7 +109,8 @@ public interface OnSelectHistoryItemListener { // Inflate search history layout. LayoutInflater inflater = LayoutInflater.from(activity); - View historyView = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_SCREEN, searchHistoryContainer, false); + View historyView = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_SCREEN, + searchHistoryContainer, false); searchHistoryContainer.addView(historyView, new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); @@ -320,17 +333,29 @@ public SearchHistoryAdapter(Context context, LinearLayout container, Collection< public void notifyDataSetChanged() { container.removeAllViews(); for (String query : history) { - View view = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_ITEM, container, false); + View view = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_ITEM, + container, false); + // Set click listener for main item (select query). + view.setOnClickListener(v -> onSelectHistoryItemListener.onSelectHistoryItem(query)); - TextView historyText = view.findViewById(ID_HISTORY_TEXT); - ImageView deleteIcon = view.findViewById(ID_DELETE_ICON); + // Set history icon. + ImageView historyIcon = view.findViewById(ID_HISTORY_ICON); + historyIcon.setImageResource(Utils.appIsUsingBoldIcons() + ? ID_SEARCH_ARROW_TIME_ICON_BOLD + : ID_SEARCH_ARROW_TIME_ICON + ); + TextView historyText = view.findViewById(ID_HISTORY_TEXT); historyText.setText(query); - // Set click listener for main item (select query). - view.setOnClickListener(v -> onSelectHistoryItemListener.onSelectHistoryItem(query)); - // Set click listener for delete icon. + ImageView deleteIcon = view.findViewById(ID_DELETE_ICON); + + deleteIcon.setImageResource(Utils.appIsUsingBoldIcons() + ? ID_SEARCH_REMOVE_ICON_BOLD + : ID_SEARCH_REMOVE_ICON + ); + deleteIcon.setOnClickListener(v -> createAndShowDialog( query, str("revanced_settings_search_remove_message"), diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java index 5a4ebd1c3b..b8cd9b6b41 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java @@ -16,7 +16,6 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -83,22 +82,15 @@ public static void setClientOrderToUse(List availableClients, Client */ private static final int MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000; + /** + * Cache limit must be greater than the maximum number of videos open at once, + * which theoretically is more than 4 (3 Shorts + one regular minimized video). + * But instead use a much larger value, to handle if a video viewed a while ago + * is somehow still referenced. Each stream is a small array of Strings + * so memory usage is not a concern. + */ private static final Map cache = Collections.synchronizedMap( - new LinkedHashMap<>(100) { - /** - * Cache limit must be greater than the maximum number of videos open at once, - * which theoretically is more than 4 (3 Shorts + one regular minimized video). - * But instead use a much larger value, to handle if a video viewed a while ago - * is somehow still referenced. Each stream is a small array of Strings - * so memory usage is not a concern. - */ - private static final int CACHE_LIMIT = 50; - - @Override - protected boolean removeEldestEntry(Entry eldest) { - return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit. - } - }); + Utils.createSizeRestrictedMap(50)); /** * Strings found in the response if the video is a livestream. diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/hide/createbutton/HideCreateButtonPatch.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/hide/createbutton/HideCreateButtonPatch.java index 47cfdf0858..29a1cc4729 100644 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/hide/createbutton/HideCreateButtonPatch.java +++ b/extensions/spotify/src/main/java/app/revanced/extension/spotify/layout/hide/createbutton/HideCreateButtonPatch.java @@ -1,6 +1,7 @@ package app.revanced.extension.spotify.layout.hide.createbutton; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.spotify.shared.ComponentFilters.ComponentFilter; import app.revanced.extension.spotify.shared.ComponentFilters.ResourceIdComponentFilter; import app.revanced.extension.spotify.shared.ComponentFilters.StringComponentFilter; @@ -16,7 +17,7 @@ public final class HideCreateButtonPatch { * The main approach used is matching the resource id for the Create button title. */ private static final List CREATE_BUTTON_COMPONENT_FILTERS = List.of( - new ResourceIdComponentFilter("navigationbar_musicappitems_create_title", "string"), + new ResourceIdComponentFilter(ResourceType.STRING, "navigationbar_musicappitems_create_title"), // Temporary fallback and fix for APKs merged with AntiSplit-M not having resources properly encoded, // and thus getting the resource identifier for the Create button title always return 0. // FIXME: Remove this once the above issue is no longer relevant. @@ -28,7 +29,7 @@ public final class HideCreateButtonPatch { * Used in older versions of the app. */ private static final ResourceIdComponentFilter OLD_CREATE_BUTTON_COMPONENT_FILTER = - new ResourceIdComponentFilter("bottom_navigation_bar_create_tab_title", "string"); + new ResourceIdComponentFilter(ResourceType.STRING, "bottom_navigation_bar_create_tab_title"); /** * Injection point. This method is called on every navigation bar item to check whether it is the Create button. diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/shared/ComponentFilters.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/shared/ComponentFilters.java index 21f1dd3e31..599c397c90 100644 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/shared/ComponentFilters.java +++ b/extensions/spotify/src/main/java/app/revanced/extension/spotify/shared/ComponentFilters.java @@ -3,6 +3,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; public final class ComponentFilters { @@ -19,21 +20,26 @@ default boolean filterUnavailable() { public static final class ResourceIdComponentFilter implements ComponentFilter { public final String resourceName; - public final String resourceType; + public final ResourceType resourceType; // Android resources are always positive, so -1 is a valid sentinel value to indicate it has not been loaded. // 0 is returned when a resource has not been found. private int resourceId = -1; @Nullable private String stringfiedResourceId; + @Deprecated public ResourceIdComponentFilter(String resourceName, String resourceType) { + this(ResourceType.valueOf(resourceType), resourceName); + } + + public ResourceIdComponentFilter(ResourceType resourceType, String resourceName) { this.resourceName = resourceName; this.resourceType = resourceType; } public int getResourceId() { if (resourceId == -1) { - resourceId = Utils.getResourceIdentifier(resourceName, resourceType); + resourceId = Utils.getResourceIdentifier(resourceType, resourceName); } return resourceId; } diff --git a/extensions/twitch/src/main/java/app/revanced/extension/twitch/Utils.java b/extensions/twitch/src/main/java/app/revanced/extension/twitch/Utils.java index 73c363ff88..5db5b8b2ff 100644 --- a/extensions/twitch/src/main/java/app/revanced/extension/twitch/Utils.java +++ b/extensions/twitch/src/main/java/app/revanced/extension/twitch/Utils.java @@ -1,14 +1,18 @@ package app.revanced.extension.twitch; +import app.revanced.extension.shared.ResourceType; + public class Utils { /* Called from SettingsPatch smali */ public static int getStringId(String name) { - return app.revanced.extension.shared.Utils.getResourceIdentifier(name, "string"); + return app.revanced.extension.shared.Utils.getResourceIdentifier( + ResourceType.STRING, name); } /* Called from SettingsPatch smali */ public static int getDrawableId(String name) { - return app.revanced.extension.shared.Utils.getResourceIdentifier(name, "drawable"); + return app.revanced.extension.shared.Utils.getResourceIdentifier( + ResourceType.DRAWABLE, name); } } diff --git a/extensions/twitch/src/main/java/app/revanced/extension/twitch/settings/TwitchActivityHook.java b/extensions/twitch/src/main/java/app/revanced/extension/twitch/settings/TwitchActivityHook.java index edf8571dbc..8886ecbe4d 100644 --- a/extensions/twitch/src/main/java/app/revanced/extension/twitch/settings/TwitchActivityHook.java +++ b/extensions/twitch/src/main/java/app/revanced/extension/twitch/settings/TwitchActivityHook.java @@ -4,19 +4,21 @@ import android.content.Intent; import android.os.Bundle; + import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; +import java.util.ArrayList; +import java.util.List; + import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.twitch.settings.preference.TwitchPreferenceFragment; import tv.twitch.android.feature.settings.menu.SettingsMenuGroup; import tv.twitch.android.settings.SettingsActivity; -import java.util.ArrayList; -import java.util.List; - /** * Hooks AppCompatActivity to inject a custom {@link TwitchPreferenceFragment}. */ @@ -108,7 +110,7 @@ public static boolean handleSettingsCreation(AppCompatActivity base) { base.getFragmentManager() .beginTransaction() - .replace(Utils.getResourceIdentifier("fragment_container", "id"), fragment) + .replace(Utils.getResourceIdentifier(ResourceType.ID, "fragment_container"), fragment) .commit(); return true; } 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 5fa4836ccc..57531bf462 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 @@ -528,14 +528,8 @@ private static class VerifiedQualities { * Cache used to verify if an alternative thumbnails exists for a given video id. */ @GuardedBy("itself") - private static final Map altVideoIdLookup = new LinkedHashMap<>(100) { - private static final int CACHE_LIMIT = 1000; - - @Override - protected boolean removeEldestEntry(Entry eldest) { - return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit. - } - }; + private static final Map altVideoIdLookup = + Utils.createSizeRestrictedMap(1000); private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, boolean returnNullIfDoesNotExist) { synchronized (altVideoIdLookup) { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeHeaderPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeHeaderPatch.java index 8d74621192..ca6bf43b12 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeHeaderPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeHeaderPatch.java @@ -7,6 +7,7 @@ import java.util.Objects; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.settings.Settings; @@ -50,7 +51,7 @@ private Integer getAttributeId() { return null; } - final int identifier = Utils.getResourceIdentifier(attributeName, "attr"); + final int identifier = Utils.getResourceIdentifier(ResourceType.ATTR, attributeName); if (identifier == 0) { // Should never happen. Logger.printException(() -> "Could not find attribute: " + drawableName); @@ -71,7 +72,7 @@ public Drawable getDrawable() { ? "_dark" : "_light"); - final int identifier = Utils.getResourceIdentifier(drawableFullName, "drawable"); + final int identifier = Utils.getResourceIdentifier(ResourceType.DRAWABLE, drawableFullName); if (identifier != 0) { return Utils.getContext().getDrawable(identifier); } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/DownloadsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/DownloadsPatch.java index e953a74cf6..4114d525c9 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/DownloadsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/DownloadsPatch.java @@ -21,7 +21,7 @@ public final class DownloadsPatch { /** * Injection point. */ - public static void activityCreated(Activity mainActivity) { + public static void setMainActivity(Activity mainActivity) { activityRef = new WeakReference<>(mainActivity); } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/FixContentProviderPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/FixContentProviderPatch.java new file mode 100644 index 0000000000..e3bf0478a8 --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/FixContentProviderPatch.java @@ -0,0 +1,24 @@ +package app.revanced.extension.youtube.patches; + +import java.util.Map; + +import app.revanced.extension.shared.Logger; + +@SuppressWarnings("unused") +public class FixContentProviderPatch { + + /** + * Injection point. + */ + public static void removeNullMapEntries(Map map) { + map.entrySet().removeIf(entry -> { + Object value = entry.getValue(); + if (value == null) { + Logger.printDebug(() -> "Removing content provider key with null value: " + entry.getKey()); + return true; + } + return false; + }); + } +} + diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HidePlayerOverlayButtonsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HidePlayerOverlayButtonsPatch.java index 7bf99f4798..a4c23aa11e 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HidePlayerOverlayButtonsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HidePlayerOverlayButtonsPatch.java @@ -7,6 +7,7 @@ import android.widget.ImageView; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.settings.Settings; @@ -29,6 +30,15 @@ public static int getCastButtonOverrideV2(int original) { return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original; } + /** + * Injection point. + */ + public static boolean getCastButtonOverrideV2(boolean original) { + if (Settings.HIDE_CAST_BUTTON.get()) return false; + + return original; + } + /** * Injection point. */ @@ -40,10 +50,10 @@ public static void hideCaptionsButton(ImageView imageView) { = Settings.HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS.get(); private static final int PLAYER_CONTROL_PREVIOUS_BUTTON_TOUCH_AREA_ID = getResourceIdentifierOrThrow( - "player_control_previous_button_touch_area", "id"); + ResourceType.ID, "player_control_previous_button_touch_area"); private static final int PLAYER_CONTROL_NEXT_BUTTON_TOUCH_AREA_ID = getResourceIdentifierOrThrow( - "player_control_next_button_touch_area", "id"); + ResourceType.ID, "player_control_next_button_touch_area"); /** * Injection point. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HideSeekbarPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HideSeekbarPatch.java index 98065d7ec0..521e7812b0 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HideSeekbarPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HideSeekbarPatch.java @@ -4,7 +4,17 @@ @SuppressWarnings("unused") public class HideSeekbarPatch { + /** + * Injection point. + */ public static boolean hideSeekbar() { return Settings.HIDE_SEEKBAR.get(); } + + /** + * Injection point. + */ + public static boolean useFullscreenLargeSeekbar(boolean original) { + return Settings.FULLSCREEN_LARGE_SEEKBAR.get(); + } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/MiniplayerPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/MiniplayerPatch.java index ac05e1540d..f24108b97f 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/MiniplayerPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/MiniplayerPatch.java @@ -15,6 +15,7 @@ import java.util.List; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.youtube.settings.Settings; @@ -115,7 +116,7 @@ public boolean isModern() { * Resource is not present in older targets, and this field will be zero. */ private static final int MODERN_OVERLAY_SUBTITLE_TEXT - = Utils.getResourceIdentifier("modern_miniplayer_subtitle_text", "id"); + = Utils.getResourceIdentifier(ResourceType.ID, "modern_miniplayer_subtitle_text"); private static final MiniplayerType CURRENT_TYPE = Settings.MINIPLAYER_TYPE.get(); @@ -378,6 +379,19 @@ public static int getMiniplayerDefaultSize(int original) { return original; } + /** + * Injection point. + */ + public static boolean allowBoldIcons(boolean original) { + if (CURRENT_TYPE == MINIMAL) { + // Minimal player does not have the correct pause/play icon (it's too large). + // Use the non bold icons instead. + return false; + } + + return original; + } + /** * Injection point. */ diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/NavigationButtonsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/NavigationButtonsPatch.java index 7021cc6f9f..76a3156596 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/NavigationButtonsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/NavigationButtonsPatch.java @@ -5,12 +5,11 @@ import android.os.Build; import android.view.View; +import android.widget.TextView; import java.util.EnumMap; import java.util.Map; -import android.widget.TextView; - import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.settings.Settings; @@ -30,13 +29,13 @@ public final class NavigationButtonsPatch { private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get(); - private static final Boolean DISABLE_TRANSLUCENT_STATUS_BAR + private static final boolean DISABLE_TRANSLUCENT_STATUS_BAR = Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get(); - private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT + private static final boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT.get(); - private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK + private static final boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK.get(); /** @@ -62,6 +61,13 @@ public static void hideNavigationButtonLabels(TextView navigationLabelsView) { hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView); } + /** + * Injection point. + */ + public static boolean useAnimatedNavigationButtons(boolean original) { + return Settings.NAVIGATION_BAR_ANIMATIONS.get(); + } + /** * Injection point. */ diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch.java index e0a1e3c700..02fc01c9fa 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch.java @@ -20,15 +20,6 @@ public enum ShortsPlayerType { REGULAR_PLAYER_FULLSCREEN } - static { - if (!VersionCheckPatch.IS_19_46_OR_GREATER - && Settings.SHORTS_PLAYER_TYPE.get() == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN) { - // User imported newer settings to an older app target. - Logger.printInfo(() -> "Resetting " + Settings.SHORTS_PLAYER_TYPE); - Settings.SHORTS_PLAYER_TYPE.resetToDefault(); - } - } - private static WeakReference mainActivityRef = new WeakReference<>(null); private static volatile boolean overrideBackPressToExit; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch.java index ff2d778741..9a7989d9a1 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch.java @@ -24,18 +24,20 @@ private static boolean isFullScreenPatchIncluded() { /** * Injection point. + * + * Returns negated value. */ - public static boolean openVideoFullscreenPortrait(boolean original) { + public static boolean doNotOpenVideoFullscreenPortrait(boolean original) { Boolean openFullscreen = openNextVideoFullscreen; if (openFullscreen != null) { openNextVideoFullscreen = null; - return openFullscreen; + return !openFullscreen; } if (!isFullScreenPatchIncluded()) { return original; } - return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get(); + return !Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get(); } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java index a969996837..be7fe91190 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java @@ -42,7 +42,7 @@ public void onGlobalLayout() { Logger.printDebug(() -> "fullscreen button visibility: " + (visibility == View.VISIBLE ? "VISIBLE" : - visibility == View.GONE ? "GONE" : "INVISIBLE")); + visibility == View.GONE ? "GONE" : "INVISIBLE")); fullscreenButtonVisibilityChanged(visibility == View.VISIBLE); } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch.java index 0260b2c69d..6f2ea968d2 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch.java @@ -1,19 +1,29 @@ package app.revanced.extension.youtube.patches; import android.app.AlertDialog; + +import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.settings.Settings; -/** @noinspection unused*/ +@SuppressWarnings("unused") public class RemoveViewerDiscretionDialogPatch { + + /** + * Injection point. + */ public static void confirmDialog(AlertDialog dialog) { - if (!Settings.REMOVE_VIEWER_DISCRETION_DIALOG.get()) { - // Since the patch replaces the AlertDialog#show() method, we need to call the original method here. - dialog.show(); + if (Settings.REMOVE_VIEWER_DISCRETION_DIALOG.get()) { + Logger.printDebug(() -> "Clicking alert dialog dismiss button"); + + final var button = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + button.setSoundEffectsEnabled(false); + button.performClick(); return; } - final var button = dialog.getButton(AlertDialog.BUTTON_POSITIVE); - button.setSoundEffectsEnabled(false); - button.performClick(); + // Since the patch replaces the AlertDialog#show() method, we need to call the original method here. + Logger.printDebug(() -> "Showing alert dialog"); + dialog.show(); } } 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 f7c0bee2d3..7c74b2929c 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 @@ -13,9 +13,11 @@ import androidx.annotation.Nullable; import java.util.Objects; +import java.util.Set; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilter; import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike; import app.revanced.extension.youtube.settings.Settings; @@ -131,6 +133,10 @@ private static CharSequence onLithoTextLoaded(@NonNull Object conversionContext, String conversionContextString = conversionContext.toString(); + if (Settings.RYD_ENABLED.get()) { // FIXME: Remove this. + Logger.printDebug(() -> "RYD conversion context: " + conversionContext); + } + if (isRollingNumber && !conversionContextString.contains("video_action_bar.e")) { return original; } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ShortsAutoplayPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ShortsAutoplayPatch.java index 9e5aff20b1..e7de421368 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ShortsAutoplayPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ShortsAutoplayPatch.java @@ -2,8 +2,6 @@ import android.app.Activity; -import androidx.annotation.Nullable; - import java.lang.ref.WeakReference; import java.util.Objects; @@ -78,7 +76,7 @@ public static void setYTShortsRepeatEnum(Enum ytEnum) { /** * Injection point. */ - public static Enum changeShortsRepeatBehavior(@Nullable Enum original) { + public static Enum changeShortsRepeatBehavior(Enum original) { try { final boolean autoplay; @@ -95,19 +93,19 @@ public static Enum changeShortsRepeatBehavior(@Nullable Enum original) { autoplay = Settings.SHORTS_AUTOPLAY.get(); } - final ShortsLoopBehavior behavior = autoplay + Enum overrideBehavior = (autoplay ? ShortsLoopBehavior.SINGLE_PLAY - : ShortsLoopBehavior.REPEAT; + : ShortsLoopBehavior.REPEAT).ytEnumValue; - if (behavior.ytEnumValue != null) { + if (overrideBehavior != null) { Logger.printDebug(() -> { String name = (original == null ? "unknown (null)" : original.name()); - return behavior == original + return overrideBehavior == original ? "Behavior setting is same as original. Using original: " + name - : "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name(); + : "Changing Shorts repeat behavior from: " + name + " to: " + overrideBehavior.name(); }); - return behavior.ytEnumValue; + return overrideBehavior; } if (original == null) { @@ -118,13 +116,12 @@ public static Enum changeShortsRepeatBehavior(@Nullable Enum original) { return unknown; } } catch (Exception ex) { - Logger.printException(() -> "changeShortsRepeatBehavior failure", ex); + Logger.printException(() -> "changeShortsRepeatState failure", ex); } return original; } - /** * Injection point. */ diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java index 2844b53db7..77d85737b5 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java @@ -19,5 +19,12 @@ private static boolean isVersionOrGreater(String version) { public static final boolean IS_19_29_OR_GREATER = isVersionOrGreater("19.29.00"); @Deprecated public static final boolean IS_19_34_OR_GREATER = isVersionOrGreater("19.34.00"); - public static final boolean IS_19_46_OR_GREATER = isVersionOrGreater("19.46.00"); + + public static final boolean IS_20_21_OR_GREATER = isVersionOrGreater("20.21.00"); + + public static final boolean IS_20_22_OR_GREATER = isVersionOrGreater("20.22.00"); + + public static final boolean IS_20_31_OR_GREATER = isVersionOrGreater("20.31.00"); + + public static final boolean IS_20_37_OR_GREATER = isVersionOrGreater("20.37.00"); } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ButtonsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ButtonsFilter.java index 3ca59ea0b1..366a4ef0c7 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ButtonsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ButtonsFilter.java @@ -1,5 +1,6 @@ package app.revanced.extension.youtube.patches.components; +import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.settings.Settings; @SuppressWarnings("unused") @@ -38,7 +39,6 @@ public ButtonsFilter() { addPathCallbacks( likeSubscribeGlow, - bufferFilterPathGroup, new StringFilterGroup( Settings.HIDE_LIKE_DISLIKE_BUTTON, "|segmented_like_dislike_button" @@ -57,6 +57,12 @@ public ButtonsFilter() { ) ); + // FIXME: 20.22+ filtering of the action buttons doesn't work because + // the buffer is the same for all buttons. + if (!VersionCheckPatch.IS_20_22_OR_GREATER) { + addPathCallbacks(bufferFilterPathGroup); + } + bufferButtonsGroupList.addAll( new ByteArrayFilterGroup( Settings.HIDE_REPORT_BUTTON, @@ -108,11 +114,13 @@ public ButtonsFilter() { } private boolean isEveryFilterGroupEnabled() { - for (var group : pathCallbacks) + for (var group : pathCallbacks) { if (!group.isEnabled()) return false; + } - for (var group : bufferButtonsGroupList) + for (var group : bufferButtonsGroupList) { if (!group.isEnabled()) return false; + } return true; } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java index 00429359f2..bcc9a4644d 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java @@ -1,5 +1,6 @@ package app.revanced.extension.youtube.patches.components; +import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_20_21_OR_GREATER; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton; import android.graphics.drawable.Drawable; @@ -370,20 +371,22 @@ boolean isFiltered(String identifier, String path, byte[] buffer, * Injection point. * Called from a different place then the other filters. */ - public static boolean filterMixPlaylists(Object conversionContext, @Nullable final byte[] bytes) { + public static boolean filterMixPlaylists(Object conversionContext, @Nullable byte[] buffer) { + // Edit: This hook may no longer be needed, and mix playlist filtering + // might be possible using the existing litho filters. try { if (!Settings.HIDE_MIX_PLAYLISTS.get()) { return false; } - if (bytes == null) { - Logger.printDebug(() -> "bytes is null"); + if (buffer == null) { + Logger.printDebug(() -> "buffer is null"); return false; } - if (mixPlaylists.check(bytes).isFiltered() + if (mixPlaylists.check(buffer).isFiltered() // Prevent hiding the description of some videos accidentally. - && !mixPlaylistsBufferExceptions.check(bytes).isFiltered() + && !mixPlaylistsBufferExceptions.check(buffer).isFiltered() // Prevent playlist items being hidden, if a mix playlist is present in it. // Check last since it requires creating a context string. // @@ -446,11 +449,23 @@ public static int hideInSearch(int height) { : height; } + private static final boolean HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS_ENABLED + = Settings.HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS.get(); + /** * Injection point. */ public static void hideInRelatedVideos(View chipView) { - Utils.hideViewBy0dpUnderCondition(Settings.HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS, chipView); + // Cannot use 0dp hide with later targets, otherwise the suggested videos + // can be shown in full screen mode. + // This behavior may also be present in earlier app targets. + if (IS_20_21_OR_GREATER) { + // FIXME: The filter bar is still briefly shown when dragging the suggested videos + // below the video player. + Utils.hideViewUnderCondition(HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS_ENABLED, chipView); + } else { + Utils.hideViewBy0dpUnderCondition(HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS_ENABLED, chipView); + } } private static final boolean HIDE_DOODLES_ENABLED = Settings.HIDE_DOODLES.get(); @@ -475,7 +490,9 @@ public static void hideShowMoreButton(View view) { && NavigationBar.isSearchBarActive() // Search bar can be active but behind the player. && !PlayerType.getCurrent().isMaximizedOrFullscreen()) { - Utils.hideViewByLayoutParams(view); + // FIXME: "Show more" button is visible hidden, + // but an empty space remains that can be clicked. + Utils.hideViewBy0dp(view); } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LithoFilterPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LithoFilterPatch.java index 2777dec111..033041c4e3 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LithoFilterPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LithoFilterPatch.java @@ -4,11 +4,16 @@ import androidx.annotation.Nullable; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.List; +import java.util.Map; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.StringTrieSearch; +import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.settings.Settings; @SuppressWarnings("unused") @@ -73,6 +78,15 @@ static void findAsciiStrings(StringBuilder builder, byte[] buffer) { } } + /** + * Placeholder for actual filters. + */ + private static final class DummyFilter extends Filter { } + + private static final Filter[] filters = new Filter[] { + new DummyFilter() // Replaced during patching, do not touch. + }; + /** * Litho layout fixed thread pool size override. *

@@ -90,25 +104,50 @@ static void findAsciiStrings(StringBuilder builder, byte[] buffer) { private static final int LITHO_LAYOUT_THREAD_POOL_SIZE = 1; /** - * Placeholder for actual filters. + * 20.22+ cannot use the thread buffer, because frequently the buffer is not correct, + * especially for components that are recreated such as dragging off screen then back on screen. + * Instead, parse the identifier found near the start of the buffer and use that to + * identify the correct buffer to use when filtering. */ - private static final class DummyFilter extends Filter { } + private static final boolean EXTRACT_IDENTIFIER_FROM_BUFFER = VersionCheckPatch.IS_20_22_OR_GREATER; - private static final Filter[] filters = new Filter[] { - new DummyFilter() // Replaced patching, do not touch. - }; + /** + * Turns on additional logging, used for development purposes only. + */ + public static final boolean DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER = false; - private static final StringTrieSearch pathSearchTree = new StringTrieSearch(); - private static final StringTrieSearch identifierSearchTree = new StringTrieSearch(); + /** + * String suffix for components. + * Can be any of: ".eml", ".e-b", ".eml-js", "e-js-b" + */ + private static final String LITHO_COMPONENT_EXTENSION = ".e"; + private static final byte[] LITHO_COMPONENT_EXTENSION_BYTES = LITHO_COMPONENT_EXTENSION.getBytes(StandardCharsets.US_ASCII); private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; /** * Because litho filtering is multi-threaded and the buffer is passed in from a different injection point, * the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads. + * Used for 20.21 and lower. */ private static final ThreadLocal bufferThreadLocal = new ThreadLocal<>(); + /** + * Identifier to protocol buffer mapping. Only used for 20.22+. + * Thread local is needed because filtering is multi-threaded and each thread can load + * a different component with the same identifier. + */ + private static final ThreadLocal> identifierToBufferThread = new ThreadLocal<>(); + + /** + * Global shared buffer. Used only if the buffer is not found in the ThreadLocal. + */ + private static final Map identifierToBufferGlobal + = Collections.synchronizedMap(createIdentifierToBufferMap()); + + private static final StringTrieSearch pathSearchTree = new StringTrieSearch(); + private static final StringTrieSearch identifierSearchTree = new StringTrieSearch(); + static { for (Filter filter : filters) { filterUsingCallbacks(identifierSearchTree, filter, @@ -160,16 +199,107 @@ private static void filterUsingCallbacks(StringTrieSearch pathSearchTree, } } + private static Map createIdentifierToBufferMap() { + // It's unclear how many items should be cached. This is a guess. + return Utils.createSizeRestrictedMap(100); + } + + /** + * Helper function that differs from {@link Character#isDigit(char)} + * as this only matches ascii and not unicode numbers. + */ + private static boolean isAsciiNumber(byte character) { + return '0' <= character && character <= '9'; + } + + private static boolean isAsciiLowerCaseLetter(byte character) { + return 'a' <= character && character <= 'z'; + } + /** * Injection point. Called off the main thread. * Targets 20.22+ */ public static void setProtoBuffer(byte[] buffer) { - // Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes. - // This is intentional, as it appears the buffer can be set once and then filtered multiple times. - // The buffer will be cleared from memory after a new buffer is set by the same thread, - // or when the calling thread eventually dies. - bufferThreadLocal.set(buffer); + if (DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER) { + StringBuilder builder = new StringBuilder(); + LithoFilterParameters.findAsciiStrings(builder, buffer); + Logger.printDebug(() -> "New buffer: " + builder); + } + + // Could use Boyer-Moore-Horspool since the string is ASCII and has a limited number of + // unique characters, but it seems to be slower since the extra overhead of checking the + // bad character array negates any performance gain of skipping a few extra subsearches. + int emlIndex = -1; + final int emlStringLength = LITHO_COMPONENT_EXTENSION_BYTES.length; + for (int i = 0, lastStartIndex = buffer.length - emlStringLength; i <= lastStartIndex; i++) { + boolean match = true; + for (int j = 0; j < emlStringLength; j++) { + if (buffer[i + j] != LITHO_COMPONENT_EXTENSION_BYTES[j]) { + match = false; + break; + } + } + if (match) { + emlIndex = i; + break; + } + } + + if (emlIndex < 0) { + // Buffer is not used for creating a new litho component. + return; + } + + int startIndex = emlIndex - 1; + while (startIndex > 0) { + final byte character = buffer[startIndex]; + int startIndexFinal = startIndex; + if (isAsciiLowerCaseLetter(character) || isAsciiNumber(character) || character == '_') { + // Valid character for the first path element. + startIndex--; + } else { + startIndex++; + break; + } + } + + // Strip away any numbers on the start of the identifier, which can + // be from random data in the buffer before the identifier starts. + while (true) { + final byte character = buffer[startIndex]; + if (isAsciiNumber(character)) { + startIndex++; + } else { + break; + } + } + + // Find the pipe character after the identifier. + int endIndex = -1; + for (int i = emlIndex, length = buffer.length; i < length; i++) { + if (buffer[i] == '|') { + endIndex = i; + break; + } + } + if (endIndex < 0) { + Logger.printException(() -> "Could not find buffer identifier"); + return; + } + + String identifier = new String(buffer, startIndex, endIndex - startIndex, StandardCharsets.US_ASCII); + if (DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER) { + Logger.printDebug(() -> "Found buffer for identifier: " + identifier); + } + identifierToBufferGlobal.put(identifier, buffer); + + Map map = identifierToBufferThread.get(); + if (map == null) { + map = createIdentifierToBufferMap(); + identifierToBufferThread.set(map); + } + map.put(identifier, buffer); } /** @@ -177,46 +307,70 @@ public static void setProtoBuffer(byte[] buffer) { * Targets 20.21 and lower. */ public static void setProtoBuffer(@Nullable ByteBuffer buffer) { - // Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes. - // This is intentional, as it appears the buffer can be set once and then filtered multiple times. - // The buffer will be cleared from memory after a new buffer is set by the same thread, - // or when the calling thread eventually dies. if (buffer == null || !buffer.hasArray()) { // It appears the buffer can be cleared out just before the call to #filter() // Ignore this null value and retain the last buffer that was set. Logger.printDebug(() -> "Ignoring null or empty buffer: " + buffer); } else { - setProtoBuffer(buffer.array()); + // Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes. + // This is intentional, as it appears the buffer can be set once and then filtered multiple times. + // The buffer will be cleared from memory after a new buffer is set by the same thread, + // or when the calling thread eventually dies. + bufferThreadLocal.set(buffer.array()); } } /** * Injection point. */ - public static boolean isFiltered(String lithoIdentifier, StringBuilder pathBuilder) { + public static boolean isFiltered(String identifier, StringBuilder pathBuilder) { try { - if (lithoIdentifier.isEmpty() && pathBuilder.length() == 0) { + if (identifier.isEmpty() || pathBuilder.length() == 0) { return false; } - byte[] buffer = bufferThreadLocal.get(); + byte[] buffer = null; + if (EXTRACT_IDENTIFIER_FROM_BUFFER) { + final int pipeIndex = identifier.indexOf('|'); + if (pipeIndex >= 0) { + // If the identifier contains no pipe, then it's not an ".eml" identifier + // and the buffer is not uniquely identified. Typically this only happens + // for subcomponents where buffer filtering is not used. + String identifierKey = identifier.substring(0, pipeIndex); + + var map = identifierToBufferThread.get(); + if (map != null) { + buffer = map.get(identifierKey); + } + + if (buffer == null) { + // Buffer for thread local not found. Use the last buffer found from any thread. + buffer = identifierToBufferGlobal.get(identifierKey); + + if (DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER && buffer == null) { + // No buffer is found for some components, such as + // shorts_lockup_cell.eml on channel profiles. + // For now, just ignore this and filter without a buffer. + Logger.printException(() -> "Could not find global buffer for identifier: " + identifier); + } + } + } + } else { + buffer = bufferThreadLocal.get(); + } + // Potentially the buffer may have been null or never set up until now. - // Use an empty buffer so the litho id/path filters still work correctly. + // Use an empty buffer so the litho id/path filters that do not use a buffer still work. if (buffer == null) { buffer = EMPTY_BYTE_ARRAY; } - LithoFilterParameters parameter = new LithoFilterParameters( - lithoIdentifier, pathBuilder.toString(), buffer); + String path = pathBuilder.toString(); + LithoFilterParameters parameter = new LithoFilterParameters(identifier, path, buffer); Logger.printDebug(() -> "Searching " + parameter); - if (identifierSearchTree.matches(parameter.identifier, parameter)) { - return true; - } - - if (pathSearchTree.matches(parameter.path, parameter)) { - return true; - } + return identifierSearchTree.matches(identifier, parameter) + || pathSearchTree.matches(path, parameter); } catch (Exception ex) { Logger.printException(() -> "isFiltered failure", ex); } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter.java index 58e3af5e6a..b50b063b09 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter.java @@ -4,15 +4,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; +import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.TrieSearch; +import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch; import app.revanced.extension.youtube.patches.VideoInformation; import app.revanced.extension.youtube.settings.Settings; -import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.TrieSearch; /** * Searches for video id's in the proto buffer of Shorts dislike. @@ -33,18 +33,7 @@ public final class ReturnYouTubeDislikeFilter extends Filter { * Cannot use {@link LinkedHashSet} because it's missing #removeEldestEntry(). */ @GuardedBy("itself") - private static final Map lastVideoIds = new LinkedHashMap<>() { - /** - * Number of video id's to keep track of for searching thru the buffer. - * A minimum value of 3 should be sufficient, but check a few more just in case. - */ - private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 5; - - @Override - protected boolean removeEldestEntry(Entry eldest) { - return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK; - } - }; + private static final Map lastVideoIds = Utils.createSizeRestrictedMap(5); /** * Injection point. 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 f41f1e3a31..9ed879b8f5 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 @@ -11,6 +11,7 @@ import java.util.List; import app.revanced.extension.shared.Logger; +import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.NavigationBar; import app.revanced.extension.youtube.shared.PlayerType; @@ -209,7 +210,11 @@ public ShortsFilter() { videoActionButton = new StringFilterGroup( null, - // Can be simply 'button.e', 'shorts_video_action_button.e' or 'reel_action_button.e' + // Can be any of: + // button.eml + // shorts_video_action_button.eml + // reel_action_button.eml + // reel_pivot_button.eml "button.e" ); @@ -220,31 +225,37 @@ public ShortsFilter() { addPathCallbacks( shortsCompactFeedVideo, joinButton, subscribeButton, paidPromotionButton, - shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar, previewComment, + suggestedAction, pausedOverlayButtons, channelBar, previewComment, fullVideoLinkLabel, videoTitle, useSoundButton, reelSoundMetadata, soundButton, infoPanel, stickers, likeFountain, likeButton, dislikeButton ); - // - // All other action buttons. - // - videoActionButtonBuffer.addAll( - new ByteArrayFilterGroup( - Settings.HIDE_SHORTS_COMMENTS_BUTTON, - "reel_comment_button", - "youtube_shorts_comment_outline" - ), - new ByteArrayFilterGroup( - Settings.HIDE_SHORTS_SHARE_BUTTON, - "reel_share_button", - "youtube_shorts_share_outline" - ), - new ByteArrayFilterGroup( - Settings.HIDE_SHORTS_REMIX_BUTTON, - "reel_remix_button", - "youtube_shorts_remix_outline" - ) - ); + // FIXME: The Shorts buffer is very different with 20.22+ and if any of these filters + // are enabled then all Shorts player vertical buttons are hidden. + if (!VersionCheckPatch.IS_20_22_OR_GREATER) { + addPathCallbacks(shortsActionBar); + + // + // All other action buttons. + // + videoActionButtonBuffer.addAll( + new ByteArrayFilterGroup( + Settings.HIDE_SHORTS_COMMENTS_BUTTON, + "reel_comment_button", + "youtube_shorts_comment_outline" + ), + new ByteArrayFilterGroup( + Settings.HIDE_SHORTS_SHARE_BUTTON, + "reel_share_button", + "youtube_shorts_share_outline" + ), + new ByteArrayFilterGroup( + Settings.HIDE_SHORTS_REMIX_BUTTON, + "reel_remix_button", + "youtube_shorts_remix_outline" + ) + ); + } // // Suggested actions. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch.java index 0ead14314c..1f726c2a5b 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch.java @@ -21,7 +21,6 @@ public class RememberVideoQualityPatch { private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI; private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE; - public static boolean shouldRememberVideoQuality() { BooleanSetting preference = ShortsPlayerState.isOpen() ? Settings.REMEMBER_SHORTS_QUALITY_LAST_SELECTED diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ReVancedSettingsIconDynamicDrawable.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ReVancedSettingsIconDynamicDrawable.java new file mode 100644 index 0000000000..0b74f45e85 --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ReVancedSettingsIconDynamicDrawable.java @@ -0,0 +1,85 @@ +package app.revanced.extension.youtube.patches.theme; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import app.revanced.extension.shared.ResourceType; +import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.settings.BaseSettings; + +/** + * Dynamic drawable that is either the regular or bolded ReVanced preference icon. + * + * This is needed because the YouTube ReVanced preference intent is an AndroidX preference, + * and AndroidX classes are not built into Android which makes programmatically changing + * the preference thru patching overly complex. This solves the problem by using a drawable + * wrapper to dynamically pick which icon drawable to use at runtime. + */ +@SuppressWarnings("unused") +public class ReVancedSettingsIconDynamicDrawable extends Drawable { + + private final Drawable icon; + + public ReVancedSettingsIconDynamicDrawable() { + final int resId = Utils.getResourceIdentifier(ResourceType.DRAWABLE, + Utils.appIsUsingBoldIcons() + ? "revanced_settings_icon_bold" + : "revanced_settings_icon" + ); + + icon = Utils.getContext().getDrawable(resId); + } + + @Override + public void draw(@NonNull Canvas canvas) { + icon.draw(canvas); + } + + @Override + public void setAlpha(int alpha) { + icon.setAlpha(alpha); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + icon.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return icon.getOpacity(); + } + + @Override + public int getIntrinsicWidth() { + return icon.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return icon.getIntrinsicHeight(); + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, bottom); + icon.setBounds(left, top, right, bottom); + } + + @Override + public void setBounds(@NonNull Rect bounds) { + super.setBounds(bounds); + icon.setBounds(bounds); + } + + @Override + public void onBoundsChange(@NonNull Rect bounds) { + super.onBoundsChange(bounds); + icon.setBounds(bounds); + } +} \ No newline at end of file diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index 2705767001..29fdb7e884 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -16,6 +16,7 @@ import java.util.Scanner; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.youtube.settings.Settings; @@ -101,16 +102,6 @@ public static int getSeekbarColor() { return customSeekbarColor; } - /** - * injection point. - */ - public static boolean useLotteLaunchSplashScreen(boolean original) { - // This method is only used for development purposes to force the old style launch screen. - // Forcing this off on some devices can cause unexplained startup crashes, - // where the lottie animation is still used even though this condition appears to bypass it. - return original; // false = drawable style, true = lottie style. - } - /** * Injection point. * Modern Lottie style animation. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java index 0a031b281a..5328fb1071 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java @@ -260,7 +260,8 @@ private static SpannableString createDislikeSpan(@NonNull Spanned oldSpannable, // middle separator String middleSeparatorString = compactLayout ? " " + MIDDLE_SEPARATOR_CHARACTER + " " - : " \u2009" + MIDDLE_SEPARATOR_CHARACTER + "\u2009 "; // u2009 = 'narrow space' character + : " \u2009\u2009" + MIDDLE_SEPARATOR_CHARACTER + "\u2009\u2009 "; // u2009 = 'narrow space' + final int shapeInsertionIndex = middleSeparatorString.length() / 2; Spannable middleSeparatorSpan = new SpannableString(middleSeparatorString); ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape()); @@ -555,7 +556,8 @@ private Spanned waitForFetchAndUpdateReplacementSpan(@NonNull Spanned original, if (originalDislikeSpan != null && replacementLikeDislikeSpan != null && spansHaveEqualTextAndColor(original, originalDislikeSpan)) { - Logger.printDebug(() -> "Replacing span with previously created dislike span of data: " + videoId); + Logger.printDebug(() -> "Replacing span: " + original + " with " + + "previously created dislike span of data: " + videoId); return replacementLikeDislikeSpan; } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java index 301b40c7a0..50d7f13763 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -44,7 +44,7 @@ import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime; -import app.revanced.extension.youtube.patches.MiniplayerPatch; +import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings; import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle; @@ -183,7 +183,7 @@ public class Settings extends BaseSettings { public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_double_tap_action", TRUE, true, new MiniplayerAnyModernAvailability()); public static final BooleanSetting MINIPLAYER_HIDE_OVERLAY_BUTTONS = new BooleanSetting("revanced_miniplayer_hide_overlay_buttons", FALSE, true, new MiniplayerHideOverlayButtonsAvailability()); public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, new MiniplayerHideSubtextsAvailability()); - public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, new MiniplayerPatch.MiniplayerHideRewindOrOverlayOpacityAvailability()); + public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, new MiniplayerHideRewindOrOverlayOpacityAvailability()); public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, new MiniplayerAnyModernAvailability()); public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, new MiniplayerHideRewindOrOverlayOpacityAvailability()); @@ -283,6 +283,7 @@ public class Settings extends BaseSettings { public static final BooleanSetting HIDE_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_hide_notifications_button", FALSE, true); public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true, "revanced_switch_create_with_notifications_button_user_dialog_message"); + public static final BooleanSetting NAVIGATION_BAR_ANIMATIONS = new BooleanSetting("revanced_navigation_bar_animations", FALSE); public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true, "revanced_disable_translucent_status_bar_user_dialog_message"); public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true); @@ -334,6 +335,7 @@ public class Settings extends BaseSettings { public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", FALSE); public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true); public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE, true); + public static final BooleanSetting FULLSCREEN_LARGE_SEEKBAR = new BooleanSetting("revanced_fullscreen_large_seekbar", FALSE); public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE); public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE); public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", FALSE); @@ -468,6 +470,13 @@ public class Settings extends BaseSettings { static { // region Migration + // 20.37+ YT removed parts of the code for the legacy tablet miniplayer. + // This check must remain until the Tablet type is eventually removed. + if (VersionCheckPatch.IS_20_37_OR_GREATER && MINIPLAYER_TYPE.get() == MiniplayerType.TABLET) { + Logger.printInfo(() -> "Resetting miniplayer tablet type"); + MINIPLAYER_TYPE.resetToDefault(); + } + // Migrate renamed change header enums. if (HEADER_LOGO.get() == HeaderLogo.REVANCED) { HEADER_LOGO.save(HeaderLogo.ROUNDED); @@ -510,6 +519,14 @@ public class Settings extends BaseSettings { SPOOF_APP_VERSION.resetToDefault(); } + if (!BaseSettings.SETTINGS_DISABLE_BOLD_ICONS.get() && SPOOF_APP_VERSION.get() + && SPOOF_APP_VERSION_TARGET.get().compareTo("19.35.00") <= 0) { + Logger.printInfo(() -> "Temporarily disabling bold icons that don't work with old spoof targets"); + // Don't save and only temporarily overwrite the value so + // if spoofing is turned off the old setting value is used. + BooleanSetting.privateSetValue(BaseSettings.SETTINGS_DISABLE_BOLD_ICONS, false); + } + // VR 1.61 is not selectable in the settings, and it's selected by spoof stream patch if needed. if (SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_1_61_48) { SPOOF_VIDEO_STREAMS_CLIENT_TYPE.resetToDefault(); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/YouTubeActivityHook.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/YouTubeActivityHook.java index 316e3f0bb6..0d7e1c70de 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/YouTubeActivityHook.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/YouTubeActivityHook.java @@ -7,6 +7,7 @@ import android.view.View; import android.widget.Toolbar; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseActivityHook; import app.revanced.extension.youtube.patches.VersionCheckPatch; @@ -15,11 +16,28 @@ import app.revanced.extension.youtube.settings.search.YouTubeSearchViewController; /** - * Hooks LicenseActivity to inject a custom {@link YouTubePreferenceFragment} with a toolbar and search functionality. + * Hooks LicenseActivity to inject a custom {@link YouTubePreferenceFragment} + * with a toolbar and search functionality. */ @SuppressWarnings("deprecation") public class YouTubeActivityHook extends BaseActivityHook { + /** + * How much time has passed since the first launch of the app. Simple check to prevent + * forcing bold icons on first launch where the settings menu is partially broken + * due to missing icon resources the client has not yet received. + */ + private static final long MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS = 30 * 1000; // 30 seconds. + + private static final boolean USE_BOLD_ICONS = VersionCheckPatch.IS_20_31_OR_GREATER + && !Settings.SETTINGS_DISABLE_BOLD_ICONS.get() + && (System.currentTimeMillis() - Settings.FIRST_TIME_APP_LAUNCHED.get()) + > MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS; + + static { + Utils.setAppIsUsingBoldIcons(USE_BOLD_ICONS); + } + private static int currentThemeValueOrdinal = -1; // Must initially be a non-valid enum ordinal value. /** @@ -44,15 +62,7 @@ protected void customizeActivityTheme(Activity activity) { final var theme = Utils.isDarkModeEnabled() ? "Theme.YouTube.Settings.Dark" : "Theme.YouTube.Settings"; - activity.setTheme(Utils.getResourceIdentifierOrThrow(theme, "style")); - } - - /** - * Returns the resource ID for the YouTube settings layout. - */ - @Override - protected int getContentViewResourceId() { - return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR; + activity.setTheme(Utils.getResourceIdentifierOrThrow(ResourceType.STYLE, theme)); } /** @@ -155,4 +165,12 @@ public static void updateLightDarkModeStatus(Enum value) { public static boolean handleBackPress() { return YouTubeSearchViewController.handleFinish(searchViewController); } + + /** + * Injection point. + */ + @SuppressWarnings("unused") + public static boolean useBoldIcons(boolean original) { + return USE_BOLD_ICONS; + } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/NavigationBar.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/NavigationBar.java index 17a509f00e..52d2f1dcf4 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/NavigationBar.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/NavigationBar.java @@ -19,8 +19,10 @@ import java.util.concurrent.TimeUnit; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseSettings; +import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.settings.Settings; @SuppressWarnings("unused") @@ -72,7 +74,7 @@ public static void setToolbar(FrameLayout layout) { */ public static boolean isSearchBarActive() { View searchbarResults = searchBarResultsRef.get(); - return searchbarResults != null && searchbarResults.getParent() != null; + return searchbarResults != null && searchbarResults.isShown(); } public static boolean isBackButtonVisible() { @@ -277,12 +279,14 @@ private static void navigationTabCreatedCallback(NavigationButton button, View t } /** - * Use the bundled non cairo filled icon instead of a custom icon. - * Use the old non cairo filled icon, which is almost identical to - * the what would be the filled cairo icon. + * Custom cairo notification filled icon to fix unpatched app missing resource. */ - private static final int fillBellCairoBlack = Utils.getResourceIdentifier( - "yt_fill_bell_black_24", "drawable"); + private static final int fillBellCairoBlack = Utils.getResourceIdentifier(ResourceType.DRAWABLE, + // The bold cairo notification filled icon is present, + // but YT still has not fixed the icon not associated to the enum. + VersionCheckPatch.IS_20_31_OR_GREATER && !Settings.SETTINGS_DISABLE_BOLD_ICONS.get() + ? "yt_fill_experimental_bell_vd_theme_24" + : "revanced_fill_bell_cairo_black_24"); /** * Injection point. @@ -290,13 +294,12 @@ private static void navigationTabCreatedCallback(NavigationButton button, View t */ @SuppressWarnings({"unchecked", "rawtypes"}) public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) { - if (fillBellCairoBlack != 0) { - // Show a popup informing this fix is no longer needed to those who might care. - if (BaseSettings.DEBUG.get() && enumMap.containsKey(tabActivityCairo)) { - Logger.printException(() -> "YouTube fixed the cairo notification icons"); - } - enumMap.putIfAbsent(tabActivityCairo, fillBellCairoBlack); + // Show a popup informing this fix is no longer needed to those who might care. + if (BaseSettings.DEBUG.get() && enumMap.containsKey(tabActivityCairo)) { + Logger.printException(() -> "YouTube fixed the notification icons"); } + + enumMap.putIfAbsent(tabActivityCairo, fillBellCairoBlack); } public enum NavigationButton { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerControlsVisibilityObserver.kt b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerControlsVisibilityObserver.kt index 26745755db..f5c51c5f39 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerControlsVisibilityObserver.kt +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerControlsVisibilityObserver.kt @@ -3,6 +3,7 @@ package app.revanced.extension.youtube.shared import android.app.Activity import android.view.View import android.view.ViewGroup +import app.revanced.extension.shared.ResourceType import app.revanced.extension.shared.Utils import java.lang.ref.WeakReference @@ -19,13 +20,13 @@ class PlayerControlsVisibilityObserverImpl( * id of the direct parent of controls_layout, R.id.youtube_controls_overlay */ private val controlsLayoutParentId = - Utils.getResourceIdentifier(activity, "youtube_controls_overlay", "id") + Utils.getResourceIdentifier(activity, ResourceType.ID, "youtube_controls_overlay") /** * id of R.id.controls_layout */ private val controlsLayoutId = - Utils.getResourceIdentifier(activity, "controls_layout", "id") + Utils.getResourceIdentifier(activity, ResourceType.ID, "controls_layout") /** * reference to the controls layout view diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerType.kt b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerType.kt index 147abc13f0..0b97b05c6e 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerType.kt +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerType.kt @@ -2,7 +2,6 @@ package app.revanced.extension.youtube.shared import app.revanced.extension.shared.Logger import app.revanced.extension.youtube.Event -import app.revanced.extension.youtube.patches.VideoInformation /** * Regular player type. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java index d903922a13..f4d5d5f62a 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java @@ -24,7 +24,6 @@ import androidx.annotation.Nullable; import java.lang.ref.WeakReference; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -150,9 +149,9 @@ public enum SponsorBlockDuration { private static long skipSegmentButtonEndTime; @Nullable private static String timeWithoutSegments; - private static int sponsorBarAbsoluteLeft; - private static int sponsorAbsoluteBarRight; - private static int sponsorBarThickness; + private static int seekbarAbsoluteLeft; + private static int seekbarAbsoluteRight; + private static int seekbarThickness; @Nullable private static SponsorSegment lastSegmentSkipped; @@ -908,31 +907,13 @@ public static void onSkipSegmentClicked(SponsorSegment segment) { * injection point. */ @SuppressWarnings("unused") - public static void setSponsorBarRect(Object self) { - try { - Field field = self.getClass().getDeclaredField("replaceMeWithsetSponsorBarRect"); - field.setAccessible(true); - Rect rect = (Rect) Objects.requireNonNull(field.get(self)); - setSponsorBarAbsoluteLeft(rect); - setSponsorBarAbsoluteRight(rect); - } catch (Exception ex) { - Logger.printException(() -> "setSponsorBarRect failure", ex); - } - } - - private static void setSponsorBarAbsoluteLeft(Rect rect) { - final int left = rect.left; - if (sponsorBarAbsoluteLeft != left) { - Logger.printDebug(() -> "setSponsorBarAbsoluteLeft: " + left); - sponsorBarAbsoluteLeft = left; - } - } - - private static void setSponsorBarAbsoluteRight(Rect rect) { - final int right = rect.right; - if (sponsorAbsoluteBarRight != right) { - Logger.printDebug(() -> "setSponsorBarAbsoluteRight: " + right); - sponsorAbsoluteBarRight = right; + public static void setSeekbarRectangle(Rect seekbarRect) { + final int left = seekbarRect.left; + final int right = seekbarRect.right; + if (seekbarAbsoluteLeft != left || seekbarAbsoluteRight != right) { + Logger.printDebug(() -> "setSeekbarRectangle left: " + left + " right: " + right); + seekbarAbsoluteLeft = left; + seekbarAbsoluteRight = right; } } @@ -940,8 +921,8 @@ private static void setSponsorBarAbsoluteRight(Rect rect) { * injection point. */ @SuppressWarnings("unused") - public static void setSponsorBarThickness(int thickness) { - sponsorBarThickness = thickness; + public static void setSeekbarThickness(int thickness) { + seekbarThickness = thickness; } /** @@ -951,8 +932,7 @@ public static void setSponsorBarThickness(int thickness) { public static String appendTimeWithoutSegments(String totalTime) { try { if (Settings.SB_ENABLED.get() && Settings.SB_VIDEO_LENGTH_WITHOUT_SEGMENTS.get() - && !TextUtils.isEmpty(totalTime) && !TextUtils.isEmpty(timeWithoutSegments) - && !isAdProgressTextVisible()) { + && !TextUtils.isEmpty(totalTime) && !TextUtils.isEmpty(timeWithoutSegments)) { // Force LTR layout, to match the same LTR video time/length layout YouTube uses for all languages return "\u202D" + totalTime + timeWithoutSegments; // u202D = left to right override } @@ -980,6 +960,7 @@ private static void calculateTimeWithoutSegments() { continue; } foundNonhighlightSegments = true; + long start = segment.start; final long end = segment.end; // To prevent nested segments from incorrectly counting additional time, @@ -1011,17 +992,17 @@ private static void calculateTimeWithoutSegments() { * Injection point. */ @SuppressWarnings("unused") - public static void drawSponsorTimeBars(final Canvas canvas, final float posY) { + public static void drawSegmentTimeBars(final Canvas canvas, final float posY) { try { - if (segments == null || isAdProgressTextVisible()) return; + if (segments == null) return; final long videoLength = VideoInformation.getVideoLength(); if (videoLength <= 0) return; - final int thicknessDiv2 = sponsorBarThickness / 2; // rounds down - final float top = posY - (sponsorBarThickness - thicknessDiv2); + final int thicknessDiv2 = seekbarThickness / 2; // Rounds down. + final float top = posY - (seekbarThickness - thicknessDiv2); final float bottom = posY + thicknessDiv2; - final float videoMillisecondsToPixels = (1f / videoLength) * (sponsorAbsoluteBarRight - sponsorBarAbsoluteLeft); - final float leftPadding = sponsorBarAbsoluteLeft; + final float videoMillisecondsToPixels = (1f / videoLength) * (seekbarAbsoluteRight - seekbarAbsoluteLeft); + final float leftPadding = seekbarAbsoluteLeft; for (SponsorSegment segment : segments) { final float left = leftPadding + segment.start * videoMillisecondsToPixels; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java index 99dbf7e185..6307aca674 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java @@ -1,16 +1,26 @@ package app.revanced.extension.youtube.sponsorblock.ui; import android.view.View; +import android.widget.ImageView; import androidx.annotation.Nullable; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; +import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController; import app.revanced.extension.youtube.videoplayer.PlayerControlButton; @SuppressWarnings("unused") public class CreateSegmentButton { + + private static final int DRAWABLE_SB_LOGO = Utils.getResourceIdentifierOrThrow( + ResourceType.DRAWABLE, Utils.appIsUsingBoldIcons() + ? "revanced_sb_logo_bold" + : "revanced_sb_logo" + ); + @Nullable private static PlayerControlButton instance; @@ -31,6 +41,14 @@ public static void initialize(View controlsView) { v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(), null ); + + // FIXME: Bold YT player icons are currently forced off. + // Enable this logic when the new player icons are not forced off. + ImageView icon = Utils.getChildViewByResourceName(controlsView, + "revanced_sb_create_segment_button"); + if (false) { + icon.setImageResource(DRAWABLE_SB_LOGO); + } } catch (Exception ex) { Logger.printException(() -> "initialize failure", ex); } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/NewSegmentLayout.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/NewSegmentLayout.java index f56281c22a..ea3204aa93 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/NewSegmentLayout.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/NewSegmentLayout.java @@ -16,6 +16,7 @@ import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.ui.Dim; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.youtube.patches.VideoInformation; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils; @@ -45,8 +46,8 @@ public NewSegmentLayout(final Context context, final AttributeSet attributeSet, final int defStyleAttr, final int defStyleRes) { super(context, attributeSet, defStyleAttr, defStyleRes); - LayoutInflater.from(context).inflate( - getResourceIdentifierOrThrow(context, "revanced_sb_new_segment", "layout"), this, true + LayoutInflater.from(context).inflate(getResourceIdentifierOrThrow(context, + ResourceType.LAYOUT, "revanced_sb_new_segment"), this, true ); initializeButton( @@ -105,7 +106,7 @@ public NewSegmentLayout(final Context context, final AttributeSet attributeSet, */ private void initializeButton(final Context context, final String resourceIdentifierName, final ButtonOnClickHandlerFunction handler, final String debugMessage) { - ImageButton button = findViewById(getResourceIdentifierOrThrow(context, resourceIdentifierName, "id")); + ImageButton button = findViewById(getResourceIdentifierOrThrow(context, ResourceType.ID, resourceIdentifierName)); // Add ripple effect RippleDrawable rippleDrawable = new RippleDrawable( diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SkipSponsorButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SkipSponsorButton.java index f7552b4cf8..9573102f32 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SkipSponsorButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SkipSponsorButton.java @@ -21,6 +21,7 @@ import java.util.Objects; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController; import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment; @@ -57,11 +58,10 @@ public SkipSponsorButton(Context context, AttributeSet attributeSet, int defStyl public SkipSponsorButton(Context context, AttributeSet attributeSet, int defStyleAttr, int defStyleRes) { super(context, attributeSet, defStyleAttr, defStyleRes); - LayoutInflater.from(context).inflate(getResourceIdentifierOrThrow(context, - "revanced_sb_skip_sponsor_button", "layout"), this, true); // layout:skip_ad_button + LayoutInflater.from(context).inflate(getResourceIdentifierOrThrow(context, ResourceType.LAYOUT, "revanced_sb_skip_sponsor_button"), this, true); // layout:skip_ad_button setMinimumHeight(getResourceDimensionPixelSize("ad_skip_ad_button_min_height")); // dimen:ad_skip_ad_button_min_height skipSponsorBtnContainer = Objects.requireNonNull(findViewById(getResourceIdentifierOrThrow( - context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container + context, ResourceType.ID, "revanced_sb_skip_sponsor_button_container"))); // id:skip_ad_button_container background = new Paint(); background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color); @@ -72,7 +72,7 @@ public SkipSponsorButton(Context context, AttributeSet attributeSet, int defStyl border.setStrokeWidth(getResourceDimension("ad_skip_ad_button_border_width")); // dimen:ad_skip_ad_button_border_width); border.setStyle(Paint.Style.STROKE); - skipSponsorTextView = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text; + skipSponsorTextView = Objects.requireNonNull(findViewById(getResourceIdentifier(context, ResourceType.ID, "revanced_sb_skip_sponsor_button_text"))); // id:skip_ad_button_text; defaultBottomMargin = getResourceDimensionPixelSize("skip_button_default_bottom_margin"); // dimen:skip_button_default_bottom_margin ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController.java index a299113ee1..6e2c9e241d 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController.java @@ -1,5 +1,6 @@ package app.revanced.extension.youtube.sponsorblock.ui; +import static app.revanced.extension.shared.Utils.getResourceIdentifier; import static app.revanced.extension.shared.Utils.getResourceIdentifierOrThrow; import android.content.Context; @@ -15,6 +16,7 @@ import java.util.Objects; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment; @@ -62,16 +64,17 @@ public static void initialize(ViewGroup viewGroup) { Context context = Utils.getContext(); RelativeLayout layout = new RelativeLayout(context); - layout.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT)); + layout.setLayoutParams(new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT)); LayoutInflater.from(context).inflate(getResourceIdentifierOrThrow( - "revanced_sb_inline_sponsor_overlay", "layout"), layout); + ResourceType.LAYOUT, "revanced_sb_inline_sponsor_overlay"), layout); inlineSponsorOverlayRef = new WeakReference<>(layout); viewGroup.addView(layout); viewGroup.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { @Override public void onChildViewAdded(View parent, View child) { - // ensure SB buttons and controls are always on top, otherwise the endscreen cards can cover the skip button + // Ensure SB buttons and controls are always on top, otherwise the end-screen cards can cover the skip button. RelativeLayout layout = inlineSponsorOverlayRef.get(); if (layout != null) { layout.bringToFront(); @@ -83,14 +86,14 @@ public void onChildViewRemoved(View parent, View child) { }); youtubeOverlaysLayoutRef = new WeakReference<>(viewGroup); - skipHighlightButtonRef = new WeakReference<>(layout.findViewById(getResourceIdentifierOrThrow( - "revanced_sb_skip_highlight_button", "id"))); + skipHighlightButtonRef = new WeakReference<>(Objects.requireNonNull(layout.findViewById( + getResourceIdentifier(ResourceType.ID, "revanced_sb_skip_highlight_button")))); - skipSponsorButtonRef = new WeakReference<>(layout.findViewById(getResourceIdentifierOrThrow( - "revanced_sb_skip_sponsor_button", "id"))); + skipSponsorButtonRef = new WeakReference<>(Objects.requireNonNull(layout.findViewById( + getResourceIdentifier(ResourceType.ID, "revanced_sb_skip_sponsor_button")))); - NewSegmentLayout newSegmentLayout = layout.findViewById(getResourceIdentifierOrThrow( - "revanced_sb_new_segment_view", "id")); + NewSegmentLayout newSegmentLayout = Objects.requireNonNull(layout.findViewById( + getResourceIdentifier(ResourceType.ID, "revanced_sb_new_segment_view"))); newSegmentLayoutRef = new WeakReference<>(newSegmentLayout); newSegmentLayout.updateLayout(); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity.kt b/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity.kt index 7fafd83a79..dde62492de 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity.kt +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity.kt @@ -8,6 +8,7 @@ import android.view.MotionEvent import android.view.ViewGroup import app.revanced.extension.shared.Logger.printDebug import app.revanced.extension.shared.Logger.printException +import app.revanced.extension.youtube.patches.VersionCheckPatch import app.revanced.extension.youtube.settings.Settings import app.revanced.extension.youtube.shared.PlayerType import app.revanced.extension.youtube.swipecontrols.controller.AudioVolumeController @@ -237,6 +238,8 @@ class SwipeControlsHostActivity : Activity() { */ @Suppress("unused") @JvmStatic - fun allowSwipeChangeVideo(original: Boolean): Boolean = Settings.SWIPE_CHANGE_VIDEO.get() + fun allowSwipeChangeVideo(original: Boolean): Boolean = + // Feature can cause crashing if forced in newer targets. + !VersionCheckPatch.IS_20_22_OR_GREATER && Settings.SWIPE_CHANGE_VIDEO.get() } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/SwipeZonesController.kt b/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/SwipeZonesController.kt index 2c2edb9591..1a69c7aabe 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/SwipeZonesController.kt +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/controller/SwipeZonesController.kt @@ -3,6 +3,7 @@ package app.revanced.extension.youtube.swipecontrols.controller import android.app.Activity import android.util.TypedValue import android.view.ViewGroup +import app.revanced.extension.shared.ResourceType import app.revanced.extension.shared.Utils import app.revanced.extension.youtube.swipecontrols.misc.Rectangle import app.revanced.extension.youtube.swipecontrols.misc.applyDimension @@ -56,7 +57,8 @@ class SwipeZonesController( /** * id for R.id.player_view */ - private val playerViewId = Utils.getResourceIdentifier(host, "player_view", "id") + private val playerViewId = Utils.getResourceIdentifier( + host, ResourceType.ID, "player_view") /** * current bounding rectangle of the player diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/views/SwipeControlsOverlayLayout.kt b/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/views/SwipeControlsOverlayLayout.kt index 71226c082d..2cec37b823 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/views/SwipeControlsOverlayLayout.kt +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/swipecontrols/views/SwipeControlsOverlayLayout.kt @@ -14,12 +14,13 @@ import android.util.AttributeSet import android.view.HapticFeedbackConstants import android.view.View import android.widget.RelativeLayout +import app.revanced.extension.shared.ResourceType import app.revanced.extension.shared.StringRef.str import app.revanced.extension.shared.Utils import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay -import kotlin.math.min import kotlin.math.max +import kotlin.math.min import kotlin.math.round /** @@ -53,7 +54,7 @@ class SwipeControlsOverlayLayout( // Function to retrieve drawable resources by name. private fun getDrawable(name: String): Drawable { val drawable = resources.getDrawable( - Utils.getResourceIdentifier(context, name, "drawable"), + Utils.getResourceIdentifier(context, ResourceType.DRAWABLE, name), context.theme, ) drawable.setTint(config.overlayTextColor) @@ -86,7 +87,7 @@ class SwipeControlsOverlayLayout( // Initialize horizontal progress bar. val screenWidth = resources.displayMetrics.widthPixels - val layoutWidth = (screenWidth * 4 / 5).toInt() // Cap at ~360dp. + val layoutWidth = (screenWidth * 4 / 5) // Cap at ~360dp. horizontalProgressView = HorizontalProgressView( context, config.overlayBackgroundOpacity, @@ -630,7 +631,7 @@ class VerticalProgressView( if (isMinimalStyle) { canvas.drawText(displayText, textX, textStartY, textPaint) } else { - val progressStartY = (iconEndY + padding).toFloat() + val progressStartY = (iconEndY + padding) val progressEndY = textStartY - textPaint.textSize - padding val progressHeight = progressEndY - progressStartY diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/LoopVideoButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/LoopVideoButton.java index 068ede3d27..5b8df3b5e6 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/LoopVideoButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/LoopVideoButton.java @@ -3,8 +3,11 @@ import static app.revanced.extension.shared.StringRef.str; import android.view.View; + import androidx.annotation.Nullable; + import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.settings.Settings; @@ -14,9 +17,9 @@ public class LoopVideoButton { private static PlayerControlButton instance; private static final int LOOP_VIDEO_ON = Utils.getResourceIdentifierOrThrow( - "revanced_loop_video_button_on", "drawable"); + ResourceType.DRAWABLE, "revanced_loop_video_button_on"); private static final int LOOP_VIDEO_OFF = Utils.getResourceIdentifierOrThrow( - "revanced_loop_video_button_off", "drawable"); + ResourceType.DRAWABLE,"revanced_loop_video_button_off"); /** * Injection point. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java index e03fa9e531..611c0b7495 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java @@ -30,6 +30,7 @@ import java.util.List; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.patches.VideoInformation; import app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch; diff --git a/gradle.properties b/gradle.properties index 259dd9b053..ed2a59377d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M org.gradle.parallel = true android.useAndroidX = true kotlin.code.style = official -version = 5.47.0-dev.5 +version = liso-6.0.0-dev.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b3d9a0a352..b5d855afc4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -revanced-patcher = "21.0.0" +revanced-patcher = "22.0.0" # Tracking https://github.com/google/smali/issues/64. #noinspection GradleDependency -smali = "3.0.5" +smali = "3.0.8" # 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818. #noinspection GradleDependency agp = "8.2.2" diff --git a/patches/api/patches.api b/patches/api/patches.api index ca59944025..fb9eb716f2 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -68,10 +68,6 @@ public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggin public static final fun getEnableAndroidDebuggingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; } -public final class app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatchKt { - public static final fun getChangeDataDirectoryLocationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/all/misc/directory/documentsprovider/ExportInternalDataDocumentsProviderPatchKt { public static final fun getExportInternalDataDocumentsProviderPatch ()Lapp/revanced/patcher/patch/ResourcePatch; } @@ -156,10 +152,6 @@ public final class app/revanced/patches/angulus/ads/RemoveAdsPatchKt { public static final fun getAngulusPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt { - public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatchKt { public static final fun getRemovePlayLimitsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -240,10 +232,6 @@ public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatc public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatchKt { - public static final fun getRestoreHiddenBackUpWhileChargingTogglePatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictionsKt { public static final fun getRemoveDeviceRestrictionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -356,14 +344,6 @@ public final class app/revanced/patches/messenger/inbox/HideInboxSubtabsPatchKt public static final fun getHideInboxSubtabsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatchKt { - public static final fun getDisableSwitchingEmojiToStickerPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatchKt { - public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/messenger/layout/HideFacebookButtonPatchKt { public static final fun getHideFacebookButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -376,14 +356,6 @@ public final class app/revanced/patches/messenger/misc/extension/ExtensionPatchK public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/messenger/navbar/RemoveMetaAITabPatchKt { - public static final fun getRemoveMetaAITabPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/meta/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt { public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -404,10 +376,6 @@ public final class app/revanced/patches/music/interaction/permanentrepeat/Perman public static final fun getPermanentRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatchKt { - public static final fun getPermanentShufflePatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/music/layout/branding/CustomBrandingPatchKt { public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; } @@ -416,10 +384,6 @@ public final class app/revanced/patches/music/layout/buttons/HideButtonsKt { public static final fun getHideButtons ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/music/layout/castbutton/HideCastButtonKt { - public static final fun getHideCastButton ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/music/layout/compactheader/HideCategoryBarKt { public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -440,11 +404,6 @@ public final class app/revanced/patches/music/layout/theme/ThemePatchKt { public static final fun getThemePatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/music/layout/upgradebutton/HideUpgradeButtonPatchKt { - public static final fun getHideUpgradeButton ()Lapp/revanced/patcher/patch/BytecodePatch; - public static final fun getRemoveUpgradeButton ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatchKt { public static final fun getBypassCertificateChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -525,10 +484,6 @@ public final class app/revanced/patches/netguard/broadcasts/removerestriction/Re public static final fun getRemoveBroadcastsRestrictionPatch ()Lapp/revanced/patcher/patch/ResourcePatch; } -public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/nunl/ads/HideAdsPatchKt { public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -537,10 +492,6 @@ public final class app/revanced/patches/nunl/firebase/SpoofCertificatePatchKt { public static final fun getSpoofCertificatePatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatchKt { public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -767,16 +718,11 @@ public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/ public static final fun getFixVideoDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatchKt { - public static final fun getFixVideoDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatchKt { public static final fun getDisableScreenshotPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } public final class app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatchKt { - public static final fun getUnlockPremiumIconPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getUnlockPremiumIconsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -814,27 +760,24 @@ public final class app/revanced/patches/shared/misc/extension/ExtensionHook { } public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt { + public static final fun activityOnCreateExtensionHook (Ljava/lang/String;)Lkotlin/jvm/functions/Function0; public static final fun extensionHook (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; - public static final fun extensionHook (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; + public static final fun extensionHook (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function0; public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lapp/revanced/patcher/Fingerprint;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; - public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; - public static final fun sharedExtensionPatch (Ljava/lang/String;[Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch; - public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch; + public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/jvm/functions/Function0; + public static final fun sharedExtensionPatch (Ljava/lang/String;[Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun sharedExtensionPatch ([Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch; } public final class app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatchKt { public static final fun getVerticalScrollPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/shared/misc/gms/FingerprintsKt { - public static final field GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME Ljava/lang/String; -} - public final class app/revanced/patches/shared/misc/gms/GmsCoreSupportPatchKt { public static final fun gmsCoreSupportPatch (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/Fingerprint;Ljava/util/Set;Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; public static synthetic fun gmsCoreSupportPatch$default (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/Fingerprint;Ljava/util/Set;Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; - public static final fun gmsCoreSupportResourcePatch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/ResourcePatch; - public static synthetic fun gmsCoreSupportResourcePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun gmsCoreSupportResourcePatch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/ResourcePatch; + public static synthetic fun gmsCoreSupportResourcePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch; } public final class app/revanced/patches/shared/misc/hex/HexPatchBuilder : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker { @@ -873,23 +816,64 @@ public final class app/revanced/patches/shared/misc/hex/Replacement { } public final class app/revanced/patches/shared/misc/mapping/ResourceElement { - public final fun component1 ()Ljava/lang/String; + public fun (Lapp/revanced/patches/shared/misc/mapping/ResourceType;Ljava/lang/String;J)V + public final fun component1 ()Lapp/revanced/patches/shared/misc/mapping/ResourceType; public final fun component2 ()Ljava/lang/String; public final fun component3 ()J - public final fun copy (Ljava/lang/String;Ljava/lang/String;J)Lapp/revanced/patches/shared/misc/mapping/ResourceElement; - public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/mapping/ResourceElement;Ljava/lang/String;Ljava/lang/String;JILjava/lang/Object;)Lapp/revanced/patches/shared/misc/mapping/ResourceElement; + public final fun copy (Lapp/revanced/patches/shared/misc/mapping/ResourceType;Ljava/lang/String;J)Lapp/revanced/patches/shared/misc/mapping/ResourceElement; + public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/mapping/ResourceElement;Lapp/revanced/patches/shared/misc/mapping/ResourceType;Ljava/lang/String;JILjava/lang/Object;)Lapp/revanced/patches/shared/misc/mapping/ResourceElement; public fun equals (Ljava/lang/Object;)Z public final fun getId ()J public final fun getName ()Ljava/lang/String; - public final fun getType ()Ljava/lang/String; + public final fun getType ()Lapp/revanced/patches/shared/misc/mapping/ResourceType; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatchKt { - public static final fun get (Ljava/util/List;Ljava/lang/String;Ljava/lang/String;)J + public static final fun getResourceElements ()Ljava/util/Collection; + public static final fun getResourceId (Lapp/revanced/patches/shared/misc/mapping/ResourceType;Ljava/lang/String;)J public static final fun getResourceMappingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; - public static final fun getResourceMappings ()Ljava/util/List; + public static final fun hasResourceId (Lapp/revanced/patches/shared/misc/mapping/ResourceType;Ljava/lang/String;)Z + public static final fun resourceLiteral (Lapp/revanced/patches/shared/misc/mapping/ResourceType;Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/LiteralFilter; + public static synthetic fun resourceLiteral$default (Lapp/revanced/patches/shared/misc/mapping/ResourceType;Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/LiteralFilter; +} + +public final class app/revanced/patches/shared/misc/mapping/ResourceType : java/lang/Enum { + public static final field ANIM Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field ANIMATOR Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field ARRAY Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field ATTR Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field BOOL Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field COLOR Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field Companion Lapp/revanced/patches/shared/misc/mapping/ResourceType$Companion; + public static final field DIMEN Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field DRAWABLE Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field FONT Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field FRACTION Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field ID Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field INTEGER Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field INTERPOLATOR Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field LAYOUT Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field MENU Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field MIPMAP Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field NAVIGATION Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field PLURALS Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field RAW Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field STRING Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field STYLE Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field STYLEABLE Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field TRANSITION Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field VALUES Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static final field XML Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getValue ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/mapping/ResourceType; + public static fun values ()[Lapp/revanced/patches/shared/misc/mapping/ResourceType; +} + +public final class app/revanced/patches/shared/misc/mapping/ResourceType$Companion { + public final fun fromValue (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/mapping/ResourceType; } public final class app/revanced/patches/shared/misc/pairip/license/DisableLicenseCheckPatchKt { @@ -899,15 +883,15 @@ public final class app/revanced/patches/shared/misc/pairip/license/DisableLicens public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt { public static final fun overrideThemeColors (Ljava/lang/String;Ljava/lang/String;)V public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch; - public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch; public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch; } public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference { public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion; - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getIcon ()Ljava/lang/String; + public final fun getIconBold ()Ljava/lang/String; public final fun getKey ()Ljava/lang/String; public final fun getLayout ()Ljava/lang/String; public final fun getSummaryKey ()Ljava/lang/String; @@ -931,9 +915,10 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getIcon ()Ljava/lang/String; + public final fun getIconBold ()Ljava/lang/String; public final fun getKey ()Ljava/lang/String; public final fun getLayout ()Ljava/lang/String; public final fun getPreferences ()Ljava/util/Set; @@ -942,8 +927,8 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP } public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { - public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V - public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V + public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V public final fun getCategories ()Ljava/util/Set; public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; @@ -951,8 +936,8 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference } public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen$Category : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { - public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceCategory; @@ -971,8 +956,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/InputTyp } public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public final fun getIntent ()Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; public fun hashCode ()I @@ -1002,22 +987,22 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref } public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getSelectable ()Z public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } public class app/revanced/patches/shared/misc/settings/preference/PreferenceCategory : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getPreferences ()Ljava/util/Set; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getPreferences ()Ljava/util/Set; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } @@ -1045,8 +1030,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SummaryT public final class app/revanced/patches/shared/misc/settings/preference/SwitchPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getSummaryOffKey ()Ljava/lang/String; public final fun getSummaryOnKey ()Ljava/lang/String; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; @@ -1054,8 +1039,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SwitchPr public final class app/revanced/patches/shared/misc/settings/preference/TextPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getInputType ()Lapp/revanced/patches/shared/misc/settings/preference/InputType; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } @@ -1092,34 +1077,14 @@ public final class app/revanced/patches/soundcloud/offlinesync/EnableOfflineSync public static final fun getEnableOfflineSync ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/spotify/layout/hide/createbutton/HideCreateButtonPatchKt { - public static final fun getHideCreateButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/spotify/layout/theme/CustomThemePatchKt { public static final fun getCustomThemePatch ()Lapp/revanced/patcher/patch/ResourcePatch; } -public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatchKt { - public static final fun getOnDemandPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt { public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/spotify/misc/fix/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatchKt { - public static final fun getSpoofPackageInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt { - public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/spotify/misc/fix/login/FixFacebookLoginPatchKt { public static final fun getFixFacebookLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1136,10 +1101,6 @@ public final class app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunch public static final fun getFixThirdPartyLaunchersWidgets ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt { - public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/stocard/layout/HideOffersTabPatchKt { public static final fun getHideOffersTabPatch ()Lapp/revanced/patcher/patch/ResourcePatch; } @@ -1356,10 +1317,6 @@ public final class app/revanced/patches/twitter/misc/links/ChangeLinkSharingDoma public static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatchKt { - public static final fun getOpenLinksWithAppChooserPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatchKt { public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1372,10 +1329,6 @@ public final class app/revanced/patches/viber/misc/navbar/HideNavigationButtonsK public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/vsco/misc/pro/UnlockProPatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatchKt { public static final fun getFirebaseGetCertPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1384,10 +1337,6 @@ public final class app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnloc public static final fun getPromoCodeUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/ad/general/HideAdsPatchKt { public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1409,7 +1358,6 @@ public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerD } public final class app/revanced/patches/youtube/interaction/doubletap/DisableChapterSkipDoubleTapPatchKt { - public static final fun getDisableChapterSkipDoubleTapPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getDisableDoubleTapActionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1486,15 +1434,7 @@ public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmod } public final class app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatchKt { - public static final fun getAlbumCardId ()J - public static final fun getBarContainerHeightId ()J - public static final fun getCrowdfundingBoxId ()J - public static final fun getExpandButtonDownId ()J - public static final fun getFabButtonId ()J - public static final fun getFilterBarHeightId ()J public static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; - public static final fun getRelatedChipCloudMarginId ()J - public static final fun getYouTubeLogo ()J } public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatchKt { @@ -1513,10 +1453,6 @@ public final class app/revanced/patches/youtube/layout/hide/rollingnumber/Disabl public static final fun getDisableRollingNumberAnimationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatchKt { - public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatchKt { public static final fun getHideShortsComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1525,10 +1461,6 @@ public final class app/revanced/patches/youtube/layout/hide/signintotvpopup/Disa public static final fun getDisableSignInToTvPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatchKt { - public static final fun getDisableSuggestedVideoEndScreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPatchKt { public static final fun getHideTimestampPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1541,14 +1473,6 @@ public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupP public static final fun getPlayerPopupPanelsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatchKt { - public static final fun getPlayerControlsBackgroundPatch ()Lapp/revanced/patcher/patch/ResourcePatch; -} - -public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenKt { - public static final fun getOpenVideosFullscreen ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatchKt { public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1603,15 +1527,6 @@ public final class app/revanced/patches/youtube/layout/startupshortsreset/Disabl public static final fun getDisableResumingShortsOnStartupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatchKt { - public static final fun getEnableTabletLayoutPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/youtube/layout/theme/LithoColorHookPatchKt { - public static final fun getLithoColorHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; - public static final fun getLithoColorOverrideHook ()Lkotlin/jvm/functions/Function2; -} - public final class app/revanced/patches/youtube/layout/theme/ThemePatchKt { public static final fun getThemePatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1628,10 +1543,6 @@ public final class app/revanced/patches/youtube/misc/announcements/Announcements public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt { - public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatchKt { public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1652,14 +1563,6 @@ public final class app/revanced/patches/youtube/misc/extension/SharedExtensionPa public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatchKt { - public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatchKt { - public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatchKt { public static final fun getFixPlaybackSpeedWhilePlayingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1711,7 +1614,6 @@ public final class app/revanced/patches/youtube/misc/playercontrols/PlayerContro public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatchKt { public static final fun getAddBottomControl ()Lkotlin/jvm/functions/Function1; public static final fun getPlayerControlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; - public static final fun getPlayerControlsResourcePatch ()Lapp/revanced/patcher/patch/ResourcePatch; public static final fun initializeBottomControl (Ljava/lang/String;)V public static final fun injectVisibilityCheckCall (Ljava/lang/String;)V } @@ -1722,9 +1624,6 @@ public final class app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPa public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPatchKt { public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/ResourcePatch; - public static final fun is_19_03_or_greater ()Z - public static final fun is_19_04_or_greater ()Z - public static final fun is_19_16_or_greater ()Z public static final fun is_19_17_or_greater ()Z public static final fun is_19_18_or_greater ()Z public static final fun is_19_23_or_greater ()Z @@ -1749,10 +1648,20 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat public static final fun is_20_10_or_greater ()Z public static final fun is_20_14_or_greater ()Z public static final fun is_20_15_or_greater ()Z -} - -public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt { - public static final fun getRemoveTrackingQueryParameterPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun is_20_19_or_greater ()Z + public static final fun is_20_20_or_greater ()Z + public static final fun is_20_21_or_greater ()Z + public static final fun is_20_22_or_greater ()Z + public static final fun is_20_26_or_greater ()Z + public static final fun is_20_28_or_greater ()Z + public static final fun is_20_30_or_greater ()Z + public static final fun is_20_31_or_greater ()Z + public static final fun is_20_34_or_greater ()Z + public static final fun is_20_37_or_greater ()Z + public static final fun is_20_39_or_greater ()Z + public static final fun is_20_41_or_greater ()Z + public static final fun is_20_45_or_greater ()Z + public static final fun is_20_46_or_greater ()Z } public final class app/revanced/patches/youtube/misc/privacy/SanitizeSharingLinksPatchKt { @@ -1794,10 +1703,6 @@ public final class app/revanced/patches/youtube/misc/spoof/UserAgentClientSpoofP public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatchKt { - public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt { public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } @@ -1806,10 +1711,6 @@ public final class app/revanced/patches/youtube/video/codecs/DisableVideoCodecsP public static final fun getDisableVideoCodecsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } -public final class app/revanced/patches/youtube/video/hdr/DisableHdrPatchKt { - public static final fun getDisableHdrPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt { public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V @@ -1866,14 +1767,6 @@ public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt { public static final fun hookVideoId (Ljava/lang/String;)V } -public final class app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatchKt { - public static final fun getRestoreOldVideoQualityMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatchKt { - public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/util/BytecodeUtilsKt { public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V @@ -1923,6 +1816,7 @@ public final class app/revanced/util/BytecodeUtilsKt { public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V + public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)V public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V @@ -1930,9 +1824,9 @@ public final class app/revanced/util/BytecodeUtilsKt { public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;J)V public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V + public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Void;)V public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V - public static synthetic fun returnEarly$default (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ZILjava/lang/Object;)V public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V @@ -1940,6 +1834,7 @@ public final class app/revanced/util/BytecodeUtilsKt { public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;J)V public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V + public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Void;)V public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V diff --git a/patches/build.gradle.kts b/patches/build.gradle.kts index 6153055b94..a34a628fc4 100644 --- a/patches/build.gradle.kts +++ b/patches/build.gradle.kts @@ -12,6 +12,12 @@ patches { } } +repositories { + mavenLocal() + gradlePluginPortal() + google() +} + dependencies { // Required due to smali, or build fails. Can be removed once smali is bumped. implementation(libs.guava) diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt deleted file mode 100644 index 8046c11fc3..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt +++ /dev/null @@ -1,19 +0,0 @@ -package app.revanced.patches.all.misc.directory - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.all.misc.directory.documentsprovider.exportInternalDataDocumentsProviderPatch - -@Suppress("unused") -@Deprecated( - "Superseded by internalDataDocumentsProviderPatch", - ReplaceWith("internalDataDocumentsProviderPatch"), -) -val changeDataDirectoryLocationPatch = bytecodePatch( - // name = "Change data directory location", - description = "Changes the data directory in the application from " + - "the app internal storage directory to /sdcard/android/data accessible by root-less devices." + - "Using this patch can cause unexpected issues with some apps.", - use = false, -) { - dependsOn(exportInternalDataDocumentsProviderPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt index 6564f4f26a..8e694f103a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt @@ -36,7 +36,7 @@ fun transformInstructionsPatch( } }.forEach { (classDef, methods) -> // And finally transform the methods... - val mutableClass = proxy(classDef).mutableClass + val mutableClass = mutableClassBy(classDef) methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod -> val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque()) diff --git a/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt deleted file mode 100644 index f8b1466228..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt +++ /dev/null @@ -1,19 +0,0 @@ -package app.revanced.patches.backdrops.misc.pro - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.Opcode - -@Deprecated("Fingerprint no longer resolves and will soon be deleted.") -internal val proUnlockFingerprint = fingerprint { - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT, - Opcode.IF_EQZ, - ) - custom { method, _ -> - method.name == "lambda\$existPurchase\$0" && - method.definingClass == "Lcom/backdrops/wallpapers/data/local/DatabaseHandlerIAB;" - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt deleted file mode 100644 index 0516e1eb72..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt +++ /dev/null @@ -1,24 +0,0 @@ -package app.revanced.patches.backdrops.misc.pro - -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.bytecodePatch -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -@Suppress("unused") -@Deprecated("This patch no longer works and will soon be deleted.") -val proUnlockPatch = bytecodePatch{ - compatibleWith("com.backdrops.wallpapers") - - execute { - val registerIndex = proUnlockFingerprint.patternMatch!!.endIndex - 1 - - proUnlockFingerprint.method.apply { - val register = getInstruction(registerIndex).registerA - addInstruction( - proUnlockFingerprint.patternMatch!!.endIndex, - "const/4 v$register, 0x1", - ) - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/cricbuzz/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/cricbuzz/misc/extension/Hooks.kt index c6520086d3..3b4e0da2c1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/cricbuzz/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/cricbuzz/misc/extension/Hooks.kt @@ -1,9 +1,5 @@ package app.revanced.patches.cricbuzz.misc.extension -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val applicationInitHook = extensionHook { - custom { method, classDef -> - method.name == "onCreate" && classDef.endsWith("/NyitoActivity;") - } -} +internal val applicationInitHook = activityOnCreateExtensionHook("/NyitoActivity;") diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt index 7f9863b74a..0c685fce9f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt @@ -20,16 +20,16 @@ val disableAdsPatch = bytecodePatch( // SharedPreferences has a debug boolean value with key "disable_ads", which maps to "DebugCategory.DISABLE_ADS". // // MonetizationDebugSettings seems to be the most general setting to work fine. - initializeMonetizationDebugSettingsFingerprint - .match(monetizationDebugSettingsToStringFingerprint.classDef) - .method.apply { - val insertIndex = initializeMonetizationDebugSettingsFingerprint.patternMatch!!.startIndex - val register = getInstruction(insertIndex).registerA + initializeMonetizationDebugSettingsFingerprint.match( + monetizationDebugSettingsToStringFingerprint.classDef + ).method.apply { + val insertIndex = initializeMonetizationDebugSettingsFingerprint.instructionMatches.first().index + val register = getInstruction(insertIndex).registerA - addInstructions( - insertIndex, - "const/4 v$register, 0x1", - ) - } + addInstructions( + insertIndex, + "const/4 v$register, 0x1", + ) + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt index 8ec0068dae..6656e66efc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt @@ -14,4 +14,4 @@ internal val initializeMonetizationDebugSettingsFingerprint = fingerprint { internal val monetizationDebugSettingsToStringFingerprint = fingerprint { strings("MonetizationDebugSettings(") // Partial string match. custom { method, _ -> method.name == "toString" } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt index 49d54c2b3f..abb222296a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt @@ -67,7 +67,7 @@ val hideSponsoredStoriesPatch = bytecodePatch( // Check if the parameter type is GraphQLStory and if sponsoredDataModelGetter returns a non-null value. // If so, hide the story by setting the visibility to StoryVisibility.GONE. getStoryVisibilityFingerprint.method.addInstructionsWithLabels( - getStoryVisibilityFingerprint.patternMatch!!.startIndex, + getStoryVisibilityFingerprint.instructionMatches.first().index, """ instance-of v0, p0, $graphQlStoryClassDescriptor if-eqz v0, :resume_normal diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt index ee7997a54e..6057630e69 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt @@ -14,7 +14,7 @@ val enableCustomTabsPatch = bytecodePatch( execute { launchCustomTabFingerprint.method.apply { - val checkIndex = launchCustomTabFingerprint.patternMatch!!.endIndex + 1 + val checkIndex = launchCustomTabFingerprint.instructionMatches.last().index + 1 val register = getInstruction(checkIndex).registerA replaceInstruction(checkIndex, "const/4 v$register, 0x1") diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt deleted file mode 100644 index 54c20a7f8c..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.revanced.patches.googlephotos.misc.preferences - -import app.revanced.patcher.fingerprint - -internal val backupPreferencesFingerprint = fingerprint { - returns("Lcom/google/android/apps/photos/backup/data/BackupPreferences;") - strings("backup_prefs_had_backup_only_when_charging_enabled") -} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt deleted file mode 100644 index ea65658bda..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt +++ /dev/null @@ -1,30 +0,0 @@ -package app.revanced.patches.googlephotos.misc.preferences - -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.indexOfFirstInstructionOrThrow -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -@Deprecated("This patch no longer works and this code will soon be deleted") -@Suppress("unused") -val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch( - description = "Restores a hidden toggle to only run backups when the device is charging." -) { - compatibleWith("com.google.android.apps.photos"("7.11.0.705590205")) - - execute { - // Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true. - backupPreferencesFingerprint.let { - it.method.apply { - val index = indexOfFirstInstructionOrThrow( - it.stringMatches!!.first().index, - Opcode.MOVE_RESULT - ) - val register = getInstruction(index).registerA - addInstruction(index + 1, "const/4 v$register, 0x1") - } - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt index bd6921bfb0..6aec4c6000 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt @@ -14,7 +14,7 @@ val removeDeviceRestrictionsPatch = bytecodePatch( compatibleWith("com.google.android.apps.recorder") execute { - val featureStringIndex = onApplicationCreateFingerprint.stringMatches!!.first().index + val featureStringIndex = onApplicationCreateFingerprint.stringMatches.first().index onApplicationCreateFingerprint.method.apply { // Remove check for device restrictions. diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt index cf8c611181..dacedb40ed 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/Fingerprints.kt @@ -1,20 +1,11 @@ package app.revanced.patches.instagram.feed import app.revanced.patcher.fingerprint -import app.revanced.patcher.patch.BytecodePatchContext internal val mainFeedRequestClassFingerprint = fingerprint { strings("Request{mReason=", ", mInstanceNumber=") } -context(BytecodePatchContext) -internal val initMainFeedRequestFingerprint get() = fingerprint { - custom { method, classDef -> - method.name == "" && - classDef == mainFeedRequestClassFingerprint.classDef - } -} - internal val mainFeedHeaderMapFinderFingerprint = fingerprint { strings("pagination_source", "FEED_REQUEST_SENT") } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt index e7af0466c9..8a5c4222f7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/feed/LimitFeedToFollowedProfiles.kt @@ -2,6 +2,7 @@ package app.revanced.patches.instagram.feed import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.fingerprint import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch import app.revanced.util.getReference @@ -41,6 +42,12 @@ val limitFeedToFollowedProfiles = bytecodePatch( } } + val initMainFeedRequestFingerprint = fingerprint { + custom { method, classDef -> + method.name == "" && + classDef == mainFeedRequestClassFingerprint.classDef + } + } initMainFeedRequestFingerprint.method.apply { // Finds the instruction where the map is being initialized in the constructor val getHeaderIndex = indexOfFirstInstructionOrThrow { diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt index cc762c1237..786d9e7398 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt @@ -11,14 +11,6 @@ internal val initializeNavigationButtonsListFingerprint = fingerprint { returns("Ljava/util/List;") } -private val navigationButtonsEnumClassDef = fingerprint { +internal val navigationButtonsEnumClassDef = fingerprint { strings("FEED", "fragment_feed", "SEARCH", "fragment_search") } - -context(BytecodePatchContext) -internal val navigationButtonsEnumInitFingerprint get() = fingerprint { - custom { method, classDef -> - method.name == "" - && classDef == navigationButtonsEnumClassDef.classDef - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt index fca74bc188..c46cd39ad9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/HideNavigationButtons.kt @@ -1,6 +1,7 @@ package app.revanced.patches.instagram.hide.navigation import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.fingerprint import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch @@ -49,13 +50,21 @@ val hideNavigationButtonsPatch = bytecodePatch( ) } - val enumNameField: String - // Get the field name which contains the name of the enum for the navigation button ("fragment_clips", "fragment_share", ...) + // Get the field name which contains the name of the enum for the navigation button + // ("fragment_clips", "fragment_share", ...) + val navigationButtonsEnumInitFingerprint = fingerprint { + custom { method, classDef -> + method.name == "" + && classDef == navigationButtonsEnumClassDef.classDef + } + } + + val enumNameField: String with(navigationButtonsEnumInitFingerprint.method) { enumNameField = indexOfFirstInstructionOrThrow { opcode == Opcode.IPUT_OBJECT && - (this as TwoRegisterInstruction).registerA == 2 // The p2 register + (this as TwoRegisterInstruction).registerA == 2 // p2 register. }.let { getInstruction(it).getReference()!!.name } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/Fingerprints.kt index 3b58aa8c2f..b39cd7b83d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/Fingerprints.kt @@ -2,7 +2,6 @@ package app.revanced.patches.instagram.hide.stories import app.revanced.patcher.fingerprint import com.android.tools.smali.dexlib2.Opcode - internal val getOrCreateAvatarViewFingerprint = fingerprint { parameters() returns("L") diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt index eca0a885f2..05591136b3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/extension/hooks/ApplicationInitHook.kt @@ -1,9 +1,7 @@ package app.revanced.patches.instagram.misc.extension.hooks -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val applicationInitHook = extensionHook { - custom { method, classDef -> - method.name == "onCreate" && classDef.endsWith("/InstagramAppShell;") - } -} +internal val applicationInitHook = activityOnCreateExtensionHook( + "/InstagramAppShell;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/PermalinkResponseJsonParserFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/PermalinkResponseJsonParserFingerprint.kt index 9e0d8e64d2..801ce5693b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/PermalinkResponseJsonParserFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/PermalinkResponseJsonParserFingerprint.kt @@ -1,6 +1,7 @@ package app.revanced.patches.instagram.misc.share import app.revanced.patcher.fingerprint +import com.google.common.util.concurrent.Striped.custom internal val permalinkResponseJsonParserFingerprint = fingerprint { strings("permalink", "PermalinkResponse") diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatch.kt index 430d2d7b72..77ee102a76 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatch.kt @@ -9,6 +9,9 @@ import app.revanced.patches.shared.PATCH_DESCRIPTION_CHANGE_LINK_SHARING_DOMAIN import app.revanced.patches.shared.PATCH_NAME_CHANGE_LINK_SHARING_DOMAIN import app.revanced.util.returnEarly +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/instagram/misc/share/domain/ChangeLinkSharingDomainPatch;" + @Suppress("unused") val changeLinkSharingDomainPatch = bytecodePatch( name = PATCH_NAME_CHANGE_LINK_SHARING_DOMAIN, diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/Fingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/Fingerprint.kt index 1337520001..f9fcc85d13 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/Fingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/domain/Fingerprint.kt @@ -3,9 +3,6 @@ package app.revanced.patches.instagram.misc.share.domain import app.revanced.patcher.fingerprint import com.android.tools.smali.dexlib2.AccessFlags -internal const val EXTENSION_CLASS_DESCRIPTOR = - "Lapp/revanced/extension/instagram/misc/share/domain/ChangeLinkSharingDomainPatch;" - internal val getCustomShareDomainFingerprint = fingerprint { accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) returns("Ljava/lang/String;") diff --git a/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt index 30242b8d94..80a9d43c78 100644 --- a/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt @@ -5,7 +5,6 @@ import app.revanced.patcher.fingerprint internal val irplusAdsFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") parameters("L", "Z") strings("TAGGED") } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt index 75912318ba..40f105525c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt @@ -4,7 +4,7 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.AccessFlags import app.revanced.patcher.fingerprint -internal val verifySignatureFingerprint = fingerprint(fuzzyPatternScanThreshold = 2) { +internal val verifySignatureFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("Z") parameters("Landroid/app/Activity;") diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt deleted file mode 100644 index b9e301725c..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt +++ /dev/null @@ -1,28 +0,0 @@ -package app.revanced.patches.messenger.inputfield - -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.bytecodePatch -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -/** - * This patch will be deleted soon. - * - * Pull requests to update this patch to the latest app target are invited. - */ -@Deprecated("This patch only works with an outdated app target that is no longer fully supported by Facebook.") -@Suppress("unused") -val disableSwitchingEmojiToStickerPatch = bytecodePatch( - description = "Disables switching from emoji to sticker search mode in message input field.", -) { - compatibleWith("com.facebook.orca"("439.0.0.29.119")) - - execute { - switchMessengeInputEmojiButtonFingerprint.method.apply { - val setStringIndex = switchMessengeInputEmojiButtonFingerprint.patternMatch!!.startIndex + 2 - val targetRegister = getInstruction(setStringIndex).registerA - - replaceInstruction(setStringIndex, "const-string v$targetRegister, \"expression\"") - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt deleted file mode 100644 index 0d5bfd58cd..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt +++ /dev/null @@ -1,16 +0,0 @@ -package app.revanced.patches.messenger.inputfield - -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.bytecodePatch - -@Suppress("unused") -val disableTypingIndicatorPatch = bytecodePatch( - name = "Disable typing indicator", - description = "Disables the indicator while typing a message.", -) { - compatibleWith("com.facebook.orca") - - execute { - sendTypingIndicatorFingerprint.method.replaceInstruction(0, "return-void") - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt deleted file mode 100644 index 75fd54f7da..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt +++ /dev/null @@ -1,31 +0,0 @@ -package app.revanced.patches.messenger.inputfield - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.dexbacked.value.DexBackedStringEncodedValue - -internal val sendTypingIndicatorFingerprint = fingerprint { - returns("V") - parameters() - custom { method, classDef -> - method.name == "run" && - classDef.fields.any { - it.name == "__redex_internal_original_name" && - (it.initialValue as? DexBackedStringEncodedValue)?.value == "ConversationTypingContext\$sendActiveStateRunnable\$1" - } - } -} - -internal val switchMessengeInputEmojiButtonFingerprint = fingerprint { - returns("V") - parameters("L", "Z") - opcodes( - Opcode.IGET_OBJECT, - Opcode.IF_EQZ, - Opcode.CONST_STRING, - Opcode.GOTO, - Opcode.CONST_STRING, - Opcode.GOTO, - ) - strings("afterTextChanged", "expression_search") -} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/Fingerprints.kt index b8dd008e4e..9300efb5cd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/Fingerprints.kt @@ -13,8 +13,8 @@ internal val getMobileConfigBoolFingerprint = fingerprint { } internal val metaAIKillSwitchCheckFingerprint = fingerprint { - strings("SearchAiagentImplementationsKillSwitch") opcodes(Opcode.CONST_WIDE) + strings("SearchAiagentImplementationsKillSwitch") } internal val extensionMethodFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/RemoveMetaAIPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/RemoveMetaAIPatch.kt index adee83a308..37d13e3dc5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/RemoveMetaAIPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/metaai/RemoveMetaAIPatch.kt @@ -36,7 +36,7 @@ val removeMetaAIPatch = bytecodePatch( // Extract the common starting digits of Meta AI flag IDs from a flag found in code. val relevantDigits = with(metaAIKillSwitchCheckFingerprint) { - method.getInstruction(patternMatch!!.startIndex).wideLiteral + method.getInstruction(patternMatch.startIndex).wideLiteral }.toString().substring(0, 7) // Replace placeholder in the extension method. diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/misc/extension/Hooks.kt index 55bd9aaa04..9c2160d040 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/misc/extension/Hooks.kt @@ -1,9 +1,7 @@ package app.revanced.patches.messenger.misc.extension -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val messengerApplicationOnCreateHook = extensionHook { - custom { method, classDef -> - method.name == "onCreate" && classDef.endsWith("/MessengerApplication;") - } -} +internal val messengerApplicationOnCreateHook = activityOnCreateExtensionHook( + "/MessengerApplication;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/navbar/RemoveMetaAITabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/navbar/RemoveMetaAITabPatch.kt deleted file mode 100644 index 280f448b39..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/navbar/RemoveMetaAITabPatch.kt +++ /dev/null @@ -1,12 +0,0 @@ -package app.revanced.patches.messenger.navbar - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.messenger.metaai.removeMetaAIPatch - -@Deprecated("Superseded by removeMetaAIPatch", ReplaceWith("removeMetaAIPatch")) -@Suppress("unused") -val removeMetaAITabPatch = bytecodePatch( - description = "Removes the 'Meta AI' tab from the navbar.", -) { - dependsOn(removeMetaAIPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/meta/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/meta/ads/Fingerprints.kt index d9e1d9e0b5..95d3359a92 100644 --- a/patches/src/main/kotlin/app/revanced/patches/meta/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/meta/ads/Fingerprints.kt @@ -1,13 +1,13 @@ -package app.revanced.patches.meta.ads - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags - -internal val adInjectorFingerprint = fingerprint { - accessFlags(AccessFlags.PRIVATE) - returns("Z") - parameters("L", "L") - strings( - "SponsoredContentController.insertItem", - ) -} +package app.revanced.patches.meta.ads + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val adInjectorFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE) + returns("Z") + parameters("L", "L") + strings( + "SponsoredContentController.insertItem", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/meta/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/meta/ads/HideAdsPatch.kt deleted file mode 100644 index 9c6839f6ce..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/meta/ads/HideAdsPatch.kt +++ /dev/null @@ -1,12 +0,0 @@ -package app.revanced.patches.meta.ads - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.returnEarly - -@Deprecated("Instead use the Instagram or Threads specific hide ads patch") -@Suppress("unused") -val hideAdsPatch = bytecodePatch { - execute { - adInjectorFingerprint.method.returnEarly(false) - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt index a2a53cabaf..676df651e0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt @@ -17,7 +17,7 @@ val forceEnglishLocalePatch = bytecodePatch( execute { syncBluetoothLanguageFingerprint.method.apply { - val resolvePhoneLocaleInstruction = syncBluetoothLanguageFingerprint.patternMatch!!.startIndex + val resolvePhoneLocaleInstruction = syncBluetoothLanguageFingerprint.instructionMatches.first().index val registerIndexToUpdate = getInstruction(resolvePhoneLocaleInstruction).registerA replaceInstruction( diff --git a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt index cd5150c164..13c491f322 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt @@ -37,7 +37,7 @@ val hideVideoAdsPatch = bytecodePatch( ) navigate(showVideoAdsParentFingerprint.originalMethod) - .to(showVideoAdsParentFingerprint.patternMatch!!.startIndex + 1) + .to(showVideoAdsParentFingerprint.instructionMatches.first().index + 1) .stop() .addInstructions( 0, diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt index addf737f84..3c0f160dc4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt @@ -39,7 +39,7 @@ val permanentRepeatPatch = bytecodePatch( SwitchPreference("revanced_music_play_permanent_repeat"), ) - val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex + val startIndex = repeatTrackFingerprint.instructionMatches.last().index val repeatIndex = startIndex + 1 repeatTrackFingerprint.method.apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt deleted file mode 100644 index f2169744fe..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt +++ /dev/null @@ -1,19 +0,0 @@ -package app.revanced.patches.music.interaction.permanentshuffle - -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint - -internal val disableShuffleFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - opcodes( - Opcode.IGET_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.SGET_OBJECT, - Opcode.IPUT_OBJECT, - Opcode.IGET_OBJECT, - Opcode.INVOKE_VIRTUAL - ) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt deleted file mode 100644 index 359ede6625..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt +++ /dev/null @@ -1,22 +0,0 @@ -package app.revanced.patches.music.interaction.permanentshuffle - -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("This patch no longer works and will be removed in the future.") -@Suppress("unused") -val permanentShufflePatch = bytecodePatch( - description = "Permanently remember your shuffle preference " + - "even if the playlist ends or another track is played." -) { - compatibleWith( - "com.google.android.apps.youtube.music"( - "7.29.52", - "8.10.52" - ) - ) - - execute { - disableShuffleFingerprint.method.addInstruction(0, "return-void") - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt index a537078e87..7208977eb1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt @@ -10,9 +10,9 @@ import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.shared.layout.branding.EXTENSION_CLASS_DESCRIPTOR import app.revanced.patches.shared.layout.branding.baseCustomBrandingPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow @@ -34,7 +34,7 @@ private val disableSplashAnimationPatch = bytecodePatch { // barely shown. Instead turn off the animation entirely (app will also launch a little faster). cairoSplashAnimationConfigFingerprint.method.apply { val literalIndex = indexOfFirstLiteralInstructionOrThrow( - resourceMappings["layout", "main_activity_launch_animation"] + getResourceId(ResourceType.LAYOUT, "main_activity_launch_animation") ) val checkCastIndex = indexOfFirstInstructionOrThrow(literalIndex) { opcode == Opcode.CHECK_CAST && diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/Fingerprints.kt index 8e8989983f..5445e19b17 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/Fingerprints.kt @@ -2,10 +2,15 @@ package app.revanced.patches.music.layout.branding import app.revanced.patcher.fingerprint import app.revanced.patches.music.shared.YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral internal val cairoSplashAnimationConfigFingerprint = fingerprint { returns("V") parameters("Landroid/os/Bundle;") + instructions( + resourceLiteral(ResourceType.LAYOUT, "main_activity_launch_animation") + ) custom { method, classDef -> method.name == "onCreate" && method.definingClass == YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/HideButtons.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/HideButtons.kt index 578eaebe2c..bf2f8513f8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/HideButtons.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/HideButtons.kt @@ -9,9 +9,9 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.settingsPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow @@ -52,11 +52,11 @@ val hideButtons = bytecodePatch( ) execute { - playerOverlayChip = resourceMappings["id", "player_overlay_chip"] - historyMenuItem = resourceMappings["id", "history_menu_item"] - offlineSettingsMenuItem = resourceMappings["id", "offline_settings_menu_item"] - searchButton = resourceMappings["layout", "search_button"] - topBarMenuItemImageView = resourceMappings["id", "top_bar_menu_item_image_view"] + playerOverlayChip = getResourceId(ResourceType.ID, "player_overlay_chip") + historyMenuItem = getResourceId(ResourceType.ID, "history_menu_item") + offlineSettingsMenuItem = getResourceId(ResourceType.ID, "offline_settings_menu_item") + searchButton = getResourceId(ResourceType.LAYOUT, "search_button") + topBarMenuItemImageView = getResourceId(ResourceType.ID, "top_bar_menu_item_image_view") addResources("music", "layout.buttons.hideButtons") diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt deleted file mode 100644 index bb6053f8ab..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.patches.music.layout.castbutton - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.music.layout.buttons.hideButtons - -@Deprecated("Patch was moved", ReplaceWith("hideButtons")) -@Suppress("unused") -val hideCastButton = bytecodePatch{ - dependsOn(hideButtons) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt index 5a62705515..92d5ad4df6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt @@ -8,8 +8,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.settingsPatch -import app.revanced.patches.shared.misc.mapping.get -import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -37,14 +37,14 @@ val hideCategoryBar = bytecodePatch( ) execute { - chipCloud = resourceMappings["layout", "chip_cloud"] - addResources("music", "layout.compactheader.hideCategoryBar") PreferenceScreen.GENERAL.addPreferences( SwitchPreference("revanced_music_hide_category_bar"), ) + chipCloud = getResourceId(ResourceType.LAYOUT, "chip_cloud") + chipCloudFingerprint.method.apply { val targetIndex = chipCloudFingerprint.patternMatch!!.endIndex val targetRegister = getInstruction(targetIndex).registerA diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt index ef44657185..7a4d7d4460 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt @@ -9,9 +9,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.settingsPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findFreeRegister @@ -24,9 +22,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference -internal var mpp_player_bottom_sheet = -1L - private set - private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/ChangeMiniplayerColorPatch;" @Suppress("unused") @@ -49,8 +44,6 @@ val changeMiniplayerColor = bytecodePatch( ) execute { - mpp_player_bottom_sheet = resourceMappings["id", "mpp_player_bottom_sheet"] - addResources("music", "layout.miniplayercolor.changeMiniplayerColor") PreferenceScreen.PLAYER.addPreferences( @@ -58,7 +51,7 @@ val changeMiniplayerColor = bytecodePatch( ) switchToggleColorFingerprint.match(miniPlayerConstructorFingerprint.classDef).let { - val relativeIndex = it.patternMatch!!.endIndex + 1 + val relativeIndex = it.patternMatch.endIndex + 1 val invokeVirtualIndex = it.method.indexOfFirstInstructionOrThrow( relativeIndex, Opcode.INVOKE_VIRTUAL diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt index 8cc939f0f3..c457f10003 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt @@ -1,14 +1,17 @@ package app.revanced.patches.music.layout.miniplayercolor import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode internal val miniPlayerConstructorFingerprint = fingerprint { returns("V") + instructions( + resourceLiteral(ResourceType.ID, "mpp_player_bottom_sheet") + ) strings("sharedToggleMenuItemMutations") - literal { mpp_player_bottom_sheet } } /** diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/Fingerprints.kt index df5dbe68d3..a8409864e7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/Fingerprints.kt @@ -24,8 +24,8 @@ internal val tabLayoutTextFingerprint = fingerprint { ) strings("FEmusic_search") custom { method, _ -> - method.containsLiteralInstruction(text1) - && indexOfGetVisibilityInstruction(method) >= 0 + method.containsLiteralInstruction(text1) && + indexOfGetVisibilityInstruction(method) >= 0 } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/NavigationBarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/NavigationBarPatch.kt index 4459700cb3..8577cedffa 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/NavigationBarPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/NavigationBarPatch.kt @@ -10,9 +10,9 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.settingsPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.util.indexOfFirstInstructionOrThrow @@ -63,7 +63,7 @@ val navigationBarPatch = bytecodePatch( ) execute { - text1 = resourceMappings["id", "text1"] + text1 = getResourceId(ResourceType.ID, "text1") addResources("music", "layout.navigationbar.navigationBarPatch") diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt index 13a50c54f1..159b9dc4d0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt @@ -41,7 +41,7 @@ val hideGetPremiumPatch = bytecodePatch( ) hideGetPremiumFingerprint.method.apply { - val insertIndex = hideGetPremiumFingerprint.patternMatch!!.endIndex + val insertIndex = hideGetPremiumFingerprint.instructionMatches.last().index val setVisibilityInstruction = getInstruction(insertIndex) val getPremiumViewRegister = setVisibilityInstruction.registerC diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/HideUpgradeButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/HideUpgradeButtonPatch.kt deleted file mode 100644 index 0b0f244975..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/HideUpgradeButtonPatch.kt +++ /dev/null @@ -1,16 +0,0 @@ -package app.revanced.patches.music.layout.upgradebutton - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.music.layout.navigationbar.navigationBarPatch - -@Deprecated("Patch is obsolete and was replaced by navigation bar patch", ReplaceWith("navigationBarPatch")) -@Suppress("unused") -val hideUpgradeButton = bytecodePatch{ - dependsOn(navigationBarPatch) -} - -@Deprecated("Patch was renamed", ReplaceWith("hideUpgradeButton")) -@Suppress("unused") -val removeUpgradeButton = bytecodePatch{ - dependsOn(hideUpgradeButton) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt index 60372d1aa0..2f429b757c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt @@ -1,19 +1,19 @@ package app.revanced.patches.music.misc.extension.hooks +import app.revanced.patcher.string import app.revanced.patches.music.shared.YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook import app.revanced.patches.shared.misc.extension.extensionHook internal val applicationInitHook = extensionHook { returns("V") parameters() - strings("activity") + instructions( + string("activity") + ) custom { method, _ -> method.name == "onCreate" } } -internal val applicationInitOnCreateHook = extensionHook { - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - method.name == "onCreate" && classDef.type == YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE - } -} +internal val applicationInitOnCreateHook = activityOnCreateExtensionHook( + YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE +) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/SettingsPatch.kt index e95db8c364..4b613827d1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/SettingsPatch.kt @@ -30,14 +30,14 @@ private val settingsResourcePatch = resourcePatch { dependsOn( resourceMappingPatch, settingsPatch( - listOf( + rootPreferences = listOf( IntentPreference( titleKey = "revanced_settings_title", summaryKey = null, intent = newIntent("revanced_settings_intent"), - ) to "settings_headers", + ) to "settings_headers" ), - preferences + preferences = preferences ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt deleted file mode 100644 index a185aca8ff..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.nfctoolsse.misc.pro - -import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint - -@Deprecated("This patch no longer works and will soon be deleted.") -internal val isLicenseRegisteredFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC) - returns("Z") - strings("kLicenseCheck") -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt deleted file mode 100644 index ac3ac09af4..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt +++ /dev/null @@ -1,20 +0,0 @@ -package app.revanced.patches.nfctoolsse.misc.pro - -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.bytecodePatch - -@Suppress("unused") -@Deprecated("This patch no longer works and will soon be deleted.") -val unlockProPatch = bytecodePatch{ - compatibleWith("com.wakdev.apps.nfctools.se") - - execute { - isLicenseRegisteredFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Hooks.kt index e83d373220..626b251095 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Hooks.kt @@ -1,9 +1,7 @@ package app.revanced.patches.nunl.ads -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val mainActivityOnCreateHook = extensionHook { - custom { method, classDef -> - classDef.endsWith("/NUApplication;") && method.name == "onCreate" - } -} +internal val mainActivityOnCreateHook = activityOnCreateExtensionHook( + "/NUApplication;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt deleted file mode 100644 index e2bffa451a..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.nyx.misc.pro - -import app.revanced.patcher.fingerprint - -internal val checkProFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("BillingManager;") && method.name == "isProVersion" - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt deleted file mode 100644 index 179e745e08..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt +++ /dev/null @@ -1,20 +0,0 @@ -package app.revanced.patches.nyx.misc.pro - -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("This patch will be removed in the future.") -@Suppress("unused") -val unlockProPatch = bytecodePatch { - compatibleWith("com.awedea.nyx") - - execute { - checkProFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt index 00b47e5161..96eb3ba0fb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt @@ -10,7 +10,7 @@ val signatureDetectionPatch = bytecodePatch( ) { execute { - val replacementIndex = checkSignatureFingerprint.patternMatch!!.endIndex + val replacementIndex = checkSignatureFingerprint.instructionMatches.last().index val checkRegister = checkSignatureFingerprint.method.getInstruction(replacementIndex).registerA checkSignatureFingerprint.method.replaceInstruction(replacementIndex, "const/4 v$checkRegister, 0x1") diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt index f6c282cbd4..a9e9e80447 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt @@ -3,7 +3,7 @@ package app.revanced.patches.photomath.misc.unlock.plus import com.android.tools.smali.dexlib2.AccessFlags import app.revanced.patcher.fingerprint -internal val isPlusUnlockedFingerprint = fingerprint{ +internal val isPlusUnlockedFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") strings("genius") diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt index 794f21bcb6..83b3e56f43 100644 --- a/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt @@ -11,7 +11,6 @@ internal val appMeasurementFingerprint = fingerprint { internal val facebookSDKFingerprint = fingerprint { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - returns("V") strings("instagram.com", "facebook.com") } diff --git a/patches/src/main/kotlin/app/revanced/patches/primevideo/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/primevideo/misc/extension/Hooks.kt index 763c2bfd50..ef22c64120 100644 --- a/patches/src/main/kotlin/app/revanced/patches/primevideo/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/primevideo/misc/extension/Hooks.kt @@ -1,9 +1,7 @@ package app.revanced.patches.primevideo.misc.extension -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val applicationInitHook = extensionHook { - custom { method, classDef -> - method.name == "onCreate" && classDef.endsWith("/SplashScreenActivity;") - } -} +internal val applicationInitHook = activityOnCreateExtensionHook( + "/SplashScreenActivity;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/Fingerprints.kt index 524beeea0e..78ce2c7db3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/Fingerprints.kt @@ -1,8 +1,6 @@ package app.revanced.patches.reddit.customclients.baconreader.fix.redgifs import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags - internal val getOkHttpClientFingerprint = fingerprint { returns("Lokhttp3/OkHttpClient;") @@ -10,4 +8,4 @@ internal val getOkHttpClientFingerprint = fingerprint { custom { method, classDef -> classDef.type == "Lcom/onelouder/baconreader/media/gfycat/RedGifsManager;" && method.name == "getOkhttpClient" } -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/misc/extension/hooks/InitHook.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/misc/extension/hooks/InitHook.kt index fe644145be..d3d4523cb9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/misc/extension/hooks/InitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/misc/extension/hooks/InitHook.kt @@ -1,9 +1,7 @@ package app.revanced.patches.reddit.customclients.baconreader.misc.extension.hooks -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val initHook = extensionHook { - custom { method, _ -> - method.definingClass == "Lcom/onelouder/baconreader/BaconReader;" && method.name == "onCreate" - } -} +internal val initHook = activityOnCreateExtensionHook( + "Lcom/onelouder/baconreader/BaconReader;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt index 8cb3f55186..c5b548f93f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt @@ -19,7 +19,7 @@ val fixAudioMissingInDownloadsPatch = bytecodePatch( ) downloadAudioFingerprint.method.apply { - downloadAudioFingerprint.stringMatches!!.forEach { match -> + downloadAudioFingerprint.stringMatches.forEach { match -> val replacement = endpointReplacements[match.string] val register = getInstruction(match.index).registerA diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt index 1e7e4e2e83..303a4ecb85 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt @@ -1,11 +1,7 @@ package app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.hooks -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val initHook = extensionHook( - insertIndexResolver = { 1 }, -) { - custom { method, _ -> - method.definingClass == "Lcom/rubenmayayo/reddit/MyApplication;" && method.name == "onCreate" - } -} +internal val initHook = activityOnCreateExtensionHook( + "Lcom/rubenmayayo/reddit/MyApplication;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt index 36fe062796..b8999d22c9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt @@ -1,15 +1,14 @@ package app.revanced.patches.reddit.customclients.infinityforreddit.subscription import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal internal val billingClientOnServiceConnectedFingerprint = fingerprint { strings("Billing service connected") } internal val startSubscriptionActivityFingerprint = fingerprint { - literal { - // Intent start flag only used in the subscription activity - 0x10008000 - } + instructions( + literal(0x10008000) // Intent start flag only used in the subscription activity + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt index 5b3029094f..3144dc775b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt @@ -1,6 +1,7 @@ package app.revanced.patches.reddit.customclients.redditisfun.api import app.revanced.patcher.fingerprint +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt index 854b7cfa5b..c7817ae3ba 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt @@ -26,7 +26,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") { getLoggedOutBearerTokenFingerprint, getRefreshTokenFingerprint, ).forEach { fingerprint -> - val clientIdIndex = fingerprint.stringMatches!!.first().index + val clientIdIndex = fingerprint.stringMatches.first().index fingerprint.method.apply { val clientIdRegister = getInstruction(clientIdIndex).registerA @@ -45,7 +45,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") { setRemoteConfigFingerprint.method.addInstructions(0, "return-void") // Prevent OAuth login being disabled remotely. - val checkIsOAuthRequestIndex = redditCheckDisableAPIFingerprint.patternMatch!!.startIndex + val checkIsOAuthRequestIndex = redditCheckDisableAPIFingerprint.instructionMatches.first().index redditCheckDisableAPIFingerprint.method.apply { val returnNextChain = getInstruction(checkIsOAuthRequestIndex).target diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt index f210a6adbe..738c6835a8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt @@ -8,7 +8,7 @@ fun disableAdsPatch(block: BytecodePatchBuilder.() -> Unit = {}) = bytecodePatch name = "Disable ads", ) { execute { - isAdsEnabledFingerprint.method.returnEarly() + isAdsEnabledFingerprint.method.returnEarly(false) } block() diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt index 0246ce7116..18c8cac7c9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt @@ -37,7 +37,7 @@ val spoofClientPatch = spoofClientPatch( returnEarly("Basic $auth") val occurrenceIndex = - getAuthorizationStringFingerprint.stringMatches!!.first().index + getAuthorizationStringFingerprint.stringMatches.first().index getAuthorizationStringFingerprint.method.apply { val authorizationStringInstruction = getInstruction(occurrenceIndex) @@ -71,7 +71,7 @@ val spoofClientPatch = spoofClientPatch( // region Patch Imgur API URL. imgurImageAPIFingerprint.let { - val apiUrlIndex = it.stringMatches!!.first().index + val apiUrlIndex = it.stringMatches.first().index it.method.replaceInstruction( apiUrlIndex, "const-string v1, \"https://api.imgur.com/3/image\"", diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt index 00244b4df7..c70b90b8f8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt @@ -1,11 +1,7 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.hooks -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val initHook = extensionHook( - insertIndexResolver = { 1 }, // Insert after call to super class. -) { - custom { method, classDef -> - method.name == "onCreate" && classDef.type == "Lcom/laurencedawson/reddit_sync/RedditApplication;" - } -} +internal val initHook = activityOnCreateExtensionHook( + "Lcom/laurencedawson/reddit_sync/RedditApplication;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/Fingerprints.kt index 16dfa46205..2115308d1b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/Fingerprints.kt @@ -2,6 +2,7 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.redgifs import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.fingerprint +import app.revanced.patcher.opcode import app.revanced.util.indexOfFirstInstruction import app.revanced.util.writeRegister import com.android.tools.smali.dexlib2.AccessFlags diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt index 2d46c284c5..073445aa38 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt @@ -29,7 +29,7 @@ val useUserEndpointPatch = bytecodePatch( oAuthUserIdRequestFingerprint, oAuthUserInfoRequestFingerprint, ).map { fingerprint -> - fingerprint.stringMatches!!.first().index to fingerprint.method + fingerprint.stringMatches.first().index to fingerprint.method }.forEach { (userPathStringIndex, method) -> val userPathStringInstruction = method.getInstruction(userPathStringIndex) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/FixVideoDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/FixVideoDownloadsPatch.kt index 64c33f308b..cb4fa24651 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/FixVideoDownloadsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/FixVideoDownloadsPatch.kt @@ -24,9 +24,9 @@ val fixVideoDownloadsPatch = bytecodePatch( ) execute { - val scanResult = parseRedditVideoNetworkResponseFingerprint.patternMatch!! - val newInstanceIndex = scanResult.startIndex - val invokeDirectIndex = scanResult.endIndex - 1 + val scanResult = parseRedditVideoNetworkResponseFingerprint.instructionMatches + val newInstanceIndex = scanResult.first().index + val invokeDirectIndex = scanResult.last().index - 1 val buildResponseInstruction = parseRedditVideoNetworkResponseFingerprint.method.getInstruction(invokeDirectIndex) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt deleted file mode 100644 index 28ad4a0dbe..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt +++ /dev/null @@ -1,18 +0,0 @@ -package app.revanced.patches.reddit.customclients.syncforreddit.fix.video - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated( - message = "Patch was move to a different package", - ReplaceWith("app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.video.fixVideoDownloadsPatch") -) -@Suppress("unused") -val fixVideoDownloadsPatch = bytecodePatch { - dependsOn(app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.video.fixVideoDownloadsPatch) - - compatibleWith( - "com.laurencedawson.reddit_sync", - "com.laurencedawson.reddit_sync.pro", - "com.laurencedawson.reddit_sync.dev", - ) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt index 992ff27e5f..5b8652a6f8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt @@ -20,9 +20,3 @@ val unlockPremiumIconsPatch = bytecodePatch( ) } } - -@Deprecated("Patch was renamed", ReplaceWith("unlockPremiumIconsPatch")) -@Suppress("unused") -val unlockPremiumIconPatch = bytecodePatch{ - dependsOn(unlockPremiumIconsPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/Fingerprints.kt index f842a45ccd..41990ae7ce 100644 --- a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/Fingerprints.kt @@ -1,12 +1,6 @@ -@file:Suppress("unused") - package app.revanced.patches.samsung.radio.misc.fix.crash import app.revanced.patcher.fingerprint -import app.revanced.patches.all.misc.transformation.IMethodCall -import app.revanced.patches.all.misc.transformation.fromMethodReference -import app.revanced.util.getReference -import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal val permissionRequestListFingerprint = fingerprint { strings( diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatch.kt index 68ef9a8011..6a1ae65519 100644 --- a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatch.kt @@ -22,33 +22,33 @@ val bypassDeviceChecksPatch = bytecodePatch( compatibleWith("com.sec.android.app.fm"("12.4.00.7", "12.3.00.13", "12.3.00.11")) execute { - // Return false = The device is not blacklisted + // Return false = The device is not blacklisted. checkDeviceFingerprint.method.apply { - // Find the first string that start with "SM-", that's the list of incompatible devices + // Find the first string that start with "SM-", that's the list of incompatible devices. val firstStringIndex = indexOfFirstInstructionOrThrow { opcode == Opcode.CONST_STRING && getReference()?.string?.startsWith("SM-") == true } - // Find the following filled-new-array (or filled-new-array/range) instruction + // Find the following filled-new-array (or filled-new-array/range) instruction. val filledNewArrayIndex = indexOfFirstInstructionOrThrow(firstStringIndex + 1) { opcode == Opcode.FILLED_NEW_ARRAY || opcode == Opcode.FILLED_NEW_ARRAY_RANGE } - // Find an available register for our use val resultRegister = findFreeRegister(filledNewArrayIndex + 1) - // Store the array there and invoke the method that we added to the class earlier + // Store the array there and invoke the method that we added to the class earlier. addInstructions( - filledNewArrayIndex + 1, """ - move-result-object v$resultRegister - invoke-static { v$resultRegister }, $EXTENSION_CLASS_DESCRIPTOR->checkIfDeviceIsIncompatible([Ljava/lang/String;)Z - move-result v$resultRegister - return v$resultRegister - """ + filledNewArrayIndex + 1, + """ + move-result-object v$resultRegister + invoke-static { v$resultRegister }, $EXTENSION_CLASS_DESCRIPTOR->checkIfDeviceIsIncompatible([Ljava/lang/String;)Z + move-result v$resultRegister + return v$resultRegister + """ ) - // Remove the instructions before our strings + // Remove the instructions before our strings. removeInstructions(0, firstStringIndex) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/Fingerprints.kt index 4768235910..8d90fb5611 100644 --- a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/restrictions/device/Fingerprints.kt @@ -57,5 +57,5 @@ private enum class MethodCall( "getCountryIso", arrayOf(), "Ljava/lang/String;", - ), -} \ No newline at end of file + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt index df927dd4a7..73347e1fc5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt @@ -1,11 +1,17 @@ package app.revanced.patches.shared import app.revanced.patcher.fingerprint +import app.revanced.patcher.string internal val castContextFetchFingerprint = fingerprint { - strings("Error fetching CastContext.") + instructions( + string("Error fetching CastContext.") + ) } internal val primeMethodFingerprint = fingerprint { - strings("com.google.android.GoogleCamera", "com.android.vending") + instructions( + string("com.android.vending"), + string("com.google.android.GoogleCamera") + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/Fingerprints.kt index 41a0427966..af61ed008d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/Fingerprints.kt @@ -1,6 +1,10 @@ package app.revanced.patches.shared.layout.theme +import app.revanced.patcher.InstructionLocation.MatchAfterImmediately +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -8,15 +12,30 @@ internal val lithoOnBoundsChangeFingerprint = fingerprint { accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) returns("V") parameters("Landroid/graphics/Rect;") - opcodes( - Opcode.IGET, - Opcode.IF_EQZ, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - Opcode.IF_NEZ, - Opcode.IGET_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.RETURN_VOID, + instructions( + fieldAccess( + opcode = Opcode.IPUT_OBJECT, + definingClass = "this", + type = "Landroid/graphics/Path;" + ), + + methodCall( + definingClass = "this", + name = "isStateful", + returnType = "Z", + location = MatchAfterWithin(5) + ), + + fieldAccess( + opcode = Opcode.IGET_OBJECT, + definingClass = "this", + type = "Landroid/graphics/Paint", + location = MatchAfterWithin(5) + ), + methodCall( + smali = "Landroid/graphics/Paint;->setColor(I)V", + location = MatchAfterImmediately() + ) ) custom { method, _ -> method.name == "onBoundsChange" diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/Fingerprints.kt index 6d6b0b662f..45b34aea9a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/Fingerprints.kt @@ -1,7 +1,7 @@ package app.revanced.patches.shared.misc.audio import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal import com.android.tools.smali.dexlib2.AccessFlags internal val formatStreamModelToStringFingerprint = fingerprint { @@ -17,10 +17,8 @@ internal val formatStreamModelToStringFingerprint = fingerprint { ) } -internal const val AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG = 45666189L - internal val selectAudioStreamFingerprint = fingerprint { - literal { - AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG - } + instructions( + literal(45666189L) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/ForceOriginalAudioPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/ForceOriginalAudioPatch.kt index 010a7143e6..08740250a1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/ForceOriginalAudioPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/ForceOriginalAudioPatch.kt @@ -66,7 +66,7 @@ internal fun forceOriginalAudioPatch( // and instead overrides to the user region language. if (fixUseLocalizedAudioTrackFlag()) { selectAudioStreamFingerprint.method.insertLiteralOverride( - AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG, + selectAudioStreamFingerprint.instructionMatches.first().index, "$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z" ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt index e6d78b7776..b4dcd66583 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt @@ -5,7 +5,6 @@ import app.revanced.patcher.FingerprintBuilder import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.fingerprint import app.revanced.patcher.patch.BytecodePatchContext -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.iface.Method @@ -21,7 +20,7 @@ internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/ */ fun sharedExtensionPatch( extensionName: String, - vararg hooks: ExtensionHook, + vararg hooks: () -> ExtensionHook, ) = bytecodePatch { dependsOn(sharedExtensionPatch(*hooks)) @@ -35,19 +34,18 @@ fun sharedExtensionPatch( * commonly for the onCreate method of exported activities. */ fun sharedExtensionPatch( - vararg hooks: ExtensionHook, + vararg hooks: () -> ExtensionHook, ) = bytecodePatch { extendWith("extensions/shared.rve") execute { - if (classes.none { EXTENSION_CLASS_DESCRIPTOR == it.type }) { - throw PatchException("Shared extension is not available. This patch can not succeed without it.") - } + // Verify the extension class exists. + classBy(EXTENSION_CLASS_DESCRIPTOR) } finalize { // The hooks are made in finalize to ensure that the context is hooked before any other patches. - hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) } + hooks.forEach { hook -> hook()(EXTENSION_CLASS_DESCRIPTOR) } // Modify Utils method to include the patches release version. revancedUtilsPatchesVersionFingerprint.method.apply { @@ -113,4 +111,31 @@ fun extensionHook( insertIndexResolver: BytecodePatchContext.(Method) -> Int = { 0 }, contextRegisterResolver: BytecodePatchContext.(Method) -> String = { "p0" }, fingerprintBuilderBlock: FingerprintBuilder.() -> Unit, -) = extensionHook(insertIndexResolver, contextRegisterResolver, fingerprint(block = fingerprintBuilderBlock)) +) = { + ExtensionHook(fingerprint(block = fingerprintBuilderBlock), insertIndexResolver, contextRegisterResolver) +} + +/** + * Creates an extension hook from a non-obfuscated activity, which typically is the main activity + * defined in the app manifest.xml file. + * + * @param activityClassType Either the full activity class type such as `Lcom/company/MainActivity;` + * or the 'ends with' string for the activity such as `/MainActivity;` + */ +fun activityOnCreateExtensionHook(activityClassType: String): () -> ExtensionHook { + if (!activityClassType.endsWith(';')) { + throw IllegalArgumentException("Activity class type does not end with semicolon: $activityClassType") + } + + val fullClassType = activityClassType.startsWith('L') + + return extensionHook { + returns("V") + parameters("Landroid/os/Bundle;") + custom { method, classDef -> + method.name == "onCreate" && + if (fullClassType) classDef.type == activityClassType + else classDef.type.endsWith(activityClassType) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt index a01839c100..b4bc540a9e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt @@ -10,15 +10,17 @@ val verticalScrollPatch = bytecodePatch( ) { execute { - canScrollVerticallyFingerprint.method.apply { - val moveResultIndex = canScrollVerticallyFingerprint.patternMatch!!.endIndex - val moveResultRegister = getInstruction(moveResultIndex).registerA + canScrollVerticallyFingerprint.let { + it.method.apply { + val moveResultIndex = it.instructionMatches.last().index + val moveResultRegister = getInstruction(moveResultIndex).registerA - val insertIndex = moveResultIndex + 1 - addInstruction( - insertIndex, - "const/4 v$moveResultRegister, 0x0", - ) + val insertIndex = moveResultIndex + 1 + addInstruction( + insertIndex, + "const/4 v$moveResultRegister, 0x0", + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt index 27eebb8ab0..e85f902bc4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt @@ -1,11 +1,8 @@ package app.revanced.patches.shared.misc.gms import app.revanced.patcher.fingerprint -import app.revanced.patches.shared.misc.gms.EXTENSION_CLASS_DESCRIPTOR import com.android.tools.smali.dexlib2.AccessFlags -const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId" - internal val googlePlayUtilityFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("I") diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt index fb773457f8..0c037fa6e3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt @@ -89,7 +89,7 @@ fun gmsCoreSupportPatch( execute { fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach { val mutableClass by lazy { - proxy(it).mutableClass + mutableClassBy(it) } it.methods.forEach classLoop@{ method -> @@ -99,12 +99,12 @@ fun gmsCoreSupportPatch( mutableClass.methods.first { MethodUtil.methodSignaturesMatch(it, method) } } - implementation.instructions.forEachIndexed insnLoop@{ index, instruction -> + implementation.instructions.forEachIndexed { index, instruction -> val string = ((instruction as? Instruction21c)?.reference as? StringReference)?.string - ?: return@insnLoop + ?: return@forEachIndexed // Apply transformation. - val transformedString = transform(string) ?: return@insnLoop + val transformedString = transform(string) ?: return@forEachIndexed mutableMethod.replaceInstruction( index, @@ -204,7 +204,15 @@ fun gmsCoreSupportPatch( primeMethodFingerprint?.let { transformPrimeMethod(packageName) } // Return these methods early to prevent the app from crashing. - earlyReturnFingerprints.forEach { it.method.returnEarly() } + earlyReturnFingerprints.forEach { + it.method.apply { + if (returnType == "Z") { + returnEarly(false) + } else { + returnEarly() + } + } + } serviceCheckFingerprint.method.returnEarly() // Google Play Utility is not present in all apps, so we need to check if it's present. @@ -499,34 +507,6 @@ private object Constants { ) } -/** - * Abstract resource patch that allows Google apps to run without root and under a different package name - * by using GmsCore instead of Google Play Services. - * - * @param fromPackageName The package name of the original app. - * @param toPackageName The package name to fall back to if no custom package name is specified in patch options. - * @param spoofedPackageSignature The signature of the package to spoof to. - * @param gmsCoreVendorGroupIdOption The option to get the vendor group ID of GmsCore. - * @param executeBlock The additional execution block of the patch. - * @param block The additional block to build the patch. - */ -fun gmsCoreSupportResourcePatch( // This is here only for binary compatibility. - fromPackageName: String, - toPackageName: String, - spoofedPackageSignature: String, - gmsCoreVendorGroupIdOption: Option, - executeBlock: ResourcePatchContext.() -> Unit = {}, - block: ResourcePatchBuilder.() -> Unit = {}, -) = gmsCoreSupportResourcePatch( - fromPackageName, - toPackageName, - spoofedPackageSignature, - gmsCoreVendorGroupIdOption, - true, - executeBlock, - block -) - /** * Abstract resource patch that allows Google apps to run without root and under a different package name * by using GmsCore instead of Google Play Services. @@ -539,8 +519,7 @@ fun gmsCoreSupportResourcePatch( // This is here only for binary compatibility. * @param executeBlock The additional execution block of the patch. * @param block The additional block to build the patch. */ -// TODO: On the next major release make this public and delete the public overloaded constructor. -internal fun gmsCoreSupportResourcePatch( +fun gmsCoreSupportResourcePatch( fromPackageName: String, toPackageName: String, spoofedPackageSignature: String, diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt index 8a9e499ddb..16b7f6fcc8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt @@ -1,64 +1,107 @@ package app.revanced.patches.shared.misc.mapping +import app.revanced.patcher.InstructionLocation +import app.revanced.patcher.LiteralFilter +import app.revanced.patcher.literal import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.resourcePatch import org.w3c.dom.Element -import java.util.* -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -// TODO: Probably renaming the patch/this is a good idea. -lateinit var resourceMappings: List - private set - -val resourceMappingPatch = resourcePatch { - val resourceMappings = Collections.synchronizedList(mutableListOf()) +import java.util.Collections + +enum class ResourceType(val value: String) { + ANIM("anim"), + ANIMATOR("animator"), + ARRAY("array"), + ATTR("attr"), + BOOL("bool"), + COLOR("color"), + DIMEN("dimen"), + DRAWABLE("drawable"), + FONT("font"), + FRACTION("fraction"), + ID("id"), + INTEGER("integer"), + INTERPOLATOR("interpolator"), + LAYOUT("layout"), + MENU("menu"), + MIPMAP("mipmap"), + NAVIGATION("navigation"), + PLURALS("plurals"), + RAW("raw"), + STRING("string"), + STYLE("style"), + STYLEABLE("styleable"), + TRANSITION("transition"), + VALUES("values"), + XML("xml"); + + companion object { + private val VALUE_MAP: Map = entries.associateBy { it.value } + + fun fromValue(value: String) = VALUE_MAP[value] + ?: throw IllegalArgumentException("Unknown resource type: $value") + } +} - execute { - val threadCount = Runtime.getRuntime().availableProcessors() - val threadPoolExecutor = Executors.newFixedThreadPool(threadCount) +data class ResourceElement(val type: ResourceType, val name: String, val id: Long) - // Save the file in memory to concurrently read from it. - val resourceXmlFile = get("res/values/public.xml").readBytes() +private lateinit var resourceMappings: MutableMap - for (threadIndex in 0 until threadCount) { - threadPoolExecutor.execute thread@{ - document(resourceXmlFile.inputStream()).use { document -> +private fun setResourceId(type: ResourceType, name: String, id: Long) { + resourceMappings[type.value + name] = ResourceElement(type, name, id) +} - val resources = document.documentElement.childNodes - val resourcesLength = resources.length - val jobSize = resourcesLength / threadCount +/** + * @return A resource id of the given resource type and name. + * @throws PatchException if the resource is not found. + */ +fun getResourceId(type: ResourceType, name: String) = resourceMappings[type.value + name]?.id + ?: throw PatchException("Could not find resource type: $type name: $name") + +/** + * @return All resource elements. If a single resource id is needed instead use [getResourceId]. + */ +fun getResourceElements() = Collections.unmodifiableCollection(resourceMappings.values) + +/** + * @return If the resource exists. + */ +fun hasResourceId(type: ResourceType, name: String) = resourceMappings[type.value + name] != null + +/** + * Identical to [LiteralFilter] except uses a decoded resource literal value. + * + * Any patch with fingerprints of this filter must + * also declare [resourceMappingPatch] as a dependency. + */ +fun resourceLiteral( + type: ResourceType, + name: String, + location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = literal({ getResourceId(type, name) }, null, location) - val batchStart = jobSize * threadIndex - val batchEnd = jobSize * (threadIndex + 1) - element@ for (i in batchStart until batchEnd) { - // Prevent out of bounds. - if (i >= resourcesLength) return@thread - val node = resources.item(i) - if (node !is Element) continue +val resourceMappingPatch = resourcePatch { + execute { + // Use a stream of the file, since no modifications are done + // and using a File parameter causes the file to be re-wrote when closed. + document(get("res/values/public.xml").inputStream()).use { document -> + val resources = document.documentElement.childNodes + val resourcesLength = resources.length + resourceMappings = HashMap(2 * resourcesLength) - val nameAttribute = node.getAttribute("name") - val typeAttribute = node.getAttribute("type") + for (i in 0 until resourcesLength) { + val node = resources.item(i) as? Element ?: continue + if (node.nodeName != "public") continue - if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) continue + val nameAttribute = node.getAttribute("name") + if (nameAttribute.startsWith("APKTOOL")) continue - val id = node.getAttribute("id").substring(2).toLong(16) + val typeAttribute = node.getAttribute("type") + val id = node.getAttribute("id").substring(2).toLong(16) - resourceMappings.add(ResourceElement(typeAttribute, nameAttribute, id)) - } - } + setResourceId(ResourceType.fromValue(typeAttribute), nameAttribute, id) } } - - threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS) - - app.revanced.patches.shared.misc.mapping.resourceMappings = resourceMappings } } - -operator fun List.get(type: String, name: String) = resourceMappings.firstOrNull { - it.type == type && it.name == name -}?.id ?: throw PatchException("Could not find resource type: $type name: $name") - -data class ResourceElement internal constructor(val type: String, val name: String, val id: Long) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/Fingerprints.kt index 47de39348c..aab50cef06 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/Fingerprints.kt @@ -1,44 +1,69 @@ package app.revanced.patches.shared.misc.privacy +import app.revanced.patcher.InstructionLocation.MatchAfterImmediately +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.checkCast +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.Opcode internal val youTubeCopyTextFingerprint = fingerprint { returns("V") parameters("L", "Ljava/util/Map;") - opcodes( - Opcode.IGET_OBJECT, // Contains the text to copy to be sanitized. - Opcode.CONST_STRING, - Opcode.INVOKE_STATIC, // ClipData.newPlainText - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.IGET_OBJECT, - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.RETURN_VOID, + instructions( + opcode(Opcode.IGET_OBJECT), + string("text/plain", location = MatchAfterWithin(2)), + methodCall( + smali = "Landroid/content/ClipData;->newPlainText(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;", + location = MatchAfterWithin(2) + ), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterWithin(2)), + methodCall( + smali = "Landroid/content/ClipboardManager;->setPrimaryClip(Landroid/content/ClipData;)V", + location = MatchAfterWithin(2) + ) ) - strings("text/plain") } internal val youTubeSystemShareSheetFingerprint = fingerprint { returns("V") parameters("L", "Ljava/util/Map;") - opcodes( - Opcode.CHECK_CAST, - Opcode.GOTO, + instructions( + methodCall( + smali = "Landroid/content/Intent;->setClassName(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;" + ), + + methodCall( + smali = "Ljava/util/List;->iterator()Ljava/util/Iterator;", + location = MatchAfterWithin(4) + ), + + fieldAccess( + opcode = Opcode.IGET_OBJECT, + type = "Ljava/lang/String;", + location = MatchAfterWithin(15) + ), + + methodCall( + smali = "Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", + location = MatchAfterWithin(15) + ) ) - strings("YTShare_Logging_Share_Intent_Endpoint_Byte_Array") } internal val youTubeShareSheetFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("L", "Ljava/util/Map;") - opcodes( - Opcode.CHECK_CAST, - Opcode.GOTO, - Opcode.MOVE_OBJECT, - Opcode.INVOKE_VIRTUAL, + instructions( + opcode(Opcode.IGET_OBJECT), + checkCast("Ljava/lang/String;", location = MatchAfterImmediately()), + opcode(Opcode.GOTO, location = MatchAfterImmediately()), + + methodCall(smali = "Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;"), + + string("YTShare_Logging_Share_Intent_Endpoint_Byte_Array") ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/SanitizeSharingLinksPatch.kt index 2fe4d7e818..63f36037e5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/SanitizeSharingLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/SanitizeSharingLinksPatch.kt @@ -1,13 +1,11 @@ package app.revanced.patches.shared.misc.privacy import app.revanced.patcher.Fingerprint -import app.revanced.patcher.Match import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS @@ -16,8 +14,9 @@ import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.util.addInstructionsAtControlFlowLabel +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/SanitizeSharingLinksPatch;" @@ -61,34 +60,39 @@ internal fun sanitizeSharingLinksPatch( } ) - fun Fingerprint.hook( - getInsertIndex: Match.PatternMatch.() -> Int, - getUrlRegister: MutableMethod.(insertIndex: Int) -> Int, - ) { - val insertIndex = patternMatch!!.getInsertIndex() - val urlRegister = method.getUrlRegister(insertIndex) + fun Fingerprint.hookUrlString(matchIndex: Int) { + val index = instructionMatches[matchIndex].index + val urlRegister = method.getInstruction(index).registerA method.addInstructions( - insertIndex, + index + 1, """ - invoke-static {v$urlRegister}, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String; + invoke-static { v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String; move-result-object v$urlRegister """ ) } - // YouTube share sheet.\ - youTubeShareSheetFingerprint.hook(getInsertIndex = { startIndex + 1 }) { insertIndex -> - getInstruction(insertIndex - 1).registerA - } + fun Fingerprint.hookIntentPutExtra(matchIndex: Int) { + val index = instructionMatches[matchIndex].index + val urlRegister = method.getInstruction(index).registerE - // Native system share sheet. - youTubeSystemShareSheetFingerprint.hook(getInsertIndex = { endIndex }) { insertIndex -> - getInstruction(insertIndex - 1).registerA + method.addInstructionsAtControlFlowLabel( + index, + """ + invoke-static { v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$urlRegister + """ + ) } - youTubeCopyTextFingerprint.hook(getInsertIndex = { startIndex + 2 }) { insertIndex -> - getInstruction(insertIndex - 2).registerA - } + // YouTube share sheet copy link. + youTubeCopyTextFingerprint.hookUrlString(0) + + // YouTube share sheet other apps. + youTubeShareSheetFingerprint.hookIntentPutExtra(3) + + // Native system share sheet. + youTubeSystemShareSheetFingerprint.hookIntentPutExtra(3) } } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt index c721044cc3..3587e9d540 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt @@ -8,7 +8,6 @@ import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.layout.branding.addBrandLicensePatch import app.revanced.patches.shared.misc.settings.preference.BasePreference -import app.revanced.patches.shared.misc.settings.preference.IntentPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.util.ResourceGroup @@ -18,15 +17,6 @@ import app.revanced.util.insertFirst import app.revanced.util.returnEarly import org.w3c.dom.Node -// TODO: Delete this on next major version bump. -@Deprecated("Use non deprecated settings patch function", - ReplaceWith("settingsPatch(listOf(rootPreference), preferences)") -) -fun settingsPatch ( - rootPreference: Pair, - preferences: Set, -) = settingsPatch(listOf(rootPreference), preferences) - private var lightThemeColor : String? = null private var darkThemeColor : String? = null @@ -71,17 +61,28 @@ fun settingsPatch ( execute { copyResources( "settings", - ResourceGroup("xml", "revanced_prefs.xml", "revanced_prefs_icons.xml"), - ResourceGroup("menu", "revanced_search_menu.xml"), + ResourceGroup("xml", + "revanced_prefs.xml", + "revanced_prefs_icons.xml", + "revanced_prefs_icons_bold.xml" + ), + ResourceGroup("menu", + "revanced_search_menu.xml" + ), ResourceGroup("drawable", // CustomListPreference resources. "revanced_ic_dialog_alert.xml", // Search resources. "revanced_settings_arrow_time.xml", + "revanced_settings_arrow_time_bold.xml", "revanced_settings_custom_checkmark.xml", + "revanced_settings_custom_checkmark_bold.xml", "revanced_settings_search_icon.xml", + "revanced_settings_search_icon_bold.xml", "revanced_settings_search_remove.xml", + "revanced_settings_search_remove_bold.xml", "revanced_settings_toolbar_arrow_left.xml", + "revanced_settings_toolbar_arrow_left_bold.xml", ), ResourceGroup("layout", "revanced_custom_list_item_checked.xml", @@ -142,20 +143,30 @@ fun settingsPatch ( // there is no easy way to change to the Android default preference layout // after the preference is inflated. // Using two different preference files is the simplest and most robust solution. - fun removeIconsAndLayout(preferences: Collection) { + fun removeIconsAndLayout(preferences: Collection, removeAllIconsAndLayout: Boolean) { preferences.forEach { preference -> preference.icon = null - preference.layout = null + if (removeAllIconsAndLayout) { + preference.iconBold = null + preference.layout = null + } if (preference is PreferenceCategory) { - removeIconsAndLayout(preference.preferences) - } - if (preference is PreferenceScreenPreference) { - removeIconsAndLayout(preference.preferences) + removeIconsAndLayout(preference.preferences, removeAllIconsAndLayout) + } else if (preference is PreferenceScreenPreference) { + removeIconsAndLayout(preference.preferences, removeAllIconsAndLayout) } } } - removeIconsAndLayout(preferences) + + // Bold icons. + removeIconsAndLayout(preferences, false) + document("res/xml/revanced_prefs_icons_bold.xml").use { document -> + val revancedPreferenceScreenNode = document.getNode("PreferenceScreen") + preferences.forEach { revancedPreferenceScreenNode.addPreference(it) } + } + + removeIconsAndLayout(preferences, true) document("res/xml/revanced_prefs.xml").use { document -> val revancedPreferenceScreenNode = document.getNode("PreferenceScreen") diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt index 70542b9b9f..b133d962ef 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt @@ -20,6 +20,7 @@ abstract class BasePreference( val titleKey: String? = "${key}_title", val summaryKey: String? = "${key}_summary", icon: String? = null, + iconBold: String? = null, layout: String? = null, val tag: String ) { @@ -27,6 +28,9 @@ abstract class BasePreference( var icon: String? = icon internal set + var iconBold: String? = iconBold + internal set + var layout: String? = layout internal set @@ -44,8 +48,9 @@ abstract class BasePreference( key?.let { setAttribute("android:key", it) } titleKey?.let { setAttribute("android:title", "@string/${titleKey}") } summaryKey?.let { addSummary(it) } - icon?.let { - setAttribute("android:icon", it) + + if (icon != null || iconBold != null) { + setAttribute("android:icon", icon ?: iconBold) setAttribute("app:iconSpaceReserved", "true") } layout?.let { setAttribute("android:layout", layout) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt index 8590e99658..24cfac2300 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt @@ -25,11 +25,12 @@ abstract class BasePreferenceScreen( titleKey: String = "${key}_title", private val summaryKey: String? = "${key}_summary", icon: String? = null, + iconBold: String? = null, layout: String? = null, preferences: MutableSet = mutableSetOf(), val categories: MutableSet = mutableSetOf(), private val sorting: Sorting = Sorting.BY_TITLE, - ) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) { + ) : BasePreferenceCollection(key, titleKey, icon, iconBold, layout, preferences) { override fun transform(): PreferenceScreenPreference { return PreferenceScreenPreference( @@ -37,6 +38,7 @@ abstract class BasePreferenceScreen( titleKey, summaryKey, icon, + iconBold, layout, sorting, // Screens and preferences are sorted at runtime by extension code, @@ -61,16 +63,18 @@ abstract class BasePreferenceScreen( key: String? = null, titleKey: String = "${key}_title", icon: String? = null, + iconBold: String? = null, layout: String? = null, preferences: MutableSet = mutableSetOf(), - ) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) { + ) : BasePreferenceCollection(key, titleKey, icon, iconBold, layout, preferences) { override fun transform(): PreferenceCategory { return PreferenceCategory( - key, - titleKey, - icon, - layout, - sorting, + key = key, + titleKey = titleKey, + icon = icon, + iconBold = iconBold, + layout = layout, + sorting = sorting, preferences = preferences, ) } @@ -92,6 +96,7 @@ abstract class BasePreferenceScreen( val key: String? = null, val titleKey: String = "${key}_title", val icon: String? = null, + val iconBold: String? = null, val layout: String? = null, val preferences: MutableSet = mutableSetOf(), ) { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt index 2aef02dc86..f0ce9e7544 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt @@ -19,10 +19,11 @@ class IntentPreference( titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", icon: String? = null, + iconBold: String? = null, layout: String? = null, tag: String = "Preference", val intent: Intent, -) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { +) : BasePreference(key, titleKey, summaryKey, icon, iconBold, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt index 2e26189b2f..d1e9da1fd6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt @@ -9,7 +9,6 @@ import org.w3c.dom.Document * * @param key The preference key. If null, other parameters must be specified. * @param titleKey The preference title key. - * @param summaryKey The preference summary key. * @param icon The preference icon resource name. * @param layout Layout declaration. * @param tag The preference class type. @@ -20,15 +19,13 @@ import org.w3c.dom.Document class ListPreference( key: String? = null, titleKey: String = "${key}_title", - /** Summary key is ignored and will be removed soon */ - //@Deprecated - summaryKey: String? = null, icon: String? = null, + iconBold: String? = null, layout: String? = null, tag: String = "app.revanced.extension.shared.settings.preference.CustomDialogListPreference", val entriesKey: String? = "${key}_entries", val entryValuesKey: String? = "${key}_entry_values" -) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { +) : BasePreference(key, titleKey, null, icon, iconBold, layout, tag) { var entries: ArrayResource? = null private set var entryValues: ArrayResource? = null diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt index b5ce554891..77f1ba3e80 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt @@ -21,10 +21,11 @@ class NonInteractivePreference( titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", icon: String? = null, + iconBold: String? = null, layout: String? = null, tag: String = "Preference", val selectable: Boolean = false, -) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { +) : BasePreference(key, titleKey, summaryKey, icon, iconBold, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { setAttribute("android:selectable", selectable.toString()) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt index ef51eca8cf..a1a3724fa8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt @@ -19,11 +19,12 @@ open class PreferenceCategory( key: String? = null, titleKey: String? = "${key}_title", icon: String? = null, + iconBold: String? = null, layout: String? = null, sorting: Sorting = Sorting.BY_TITLE, tag: String = "PreferenceCategory", val preferences: Set -) : BasePreference(sorting.appendSortType(key), titleKey, null, icon, layout, tag) { +) : BasePreference(sorting.appendSortType(key), titleKey, null, icon, iconBold, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt index a37e92947b..6f757a1b52 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt @@ -22,6 +22,7 @@ open class PreferenceScreenPreference( titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", icon: String? = null, + iconBold: String? = null, layout: String? = null, sorting: Sorting = Sorting.BY_TITLE, tag: String = "PreferenceScreen", @@ -32,7 +33,7 @@ open class PreferenceScreenPreference( // or adding new attributes to the attrs.xml file. // Since the key value is not currently used by the extensions, // for now it's much simpler to modify the key to include the sort parameter. -) : BasePreference(sorting.appendSortType(key), titleKey, summaryKey, icon, layout, tag) { +) : BasePreference(sorting.appendSortType(key), titleKey, summaryKey, icon, iconBold, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { preferences.forEach { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt index df9accd4ba..429eac6839 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt @@ -20,10 +20,11 @@ class SwitchPreference( titleKey: String = "${key}_title", tag: String = "SwitchPreference", icon: String? = null, + iconBold: String? = null, layout: String? = null, val summaryOnKey: String = "${key}_summary_on", val summaryOffKey: String = "${key}_summary_off" -) : BasePreference(key, titleKey, null, icon, layout, tag) { +) : BasePreference(key, titleKey, null, icon, iconBold, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { addSummary(summaryOnKey, SummaryType.ON) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt index 397d4bd437..8c74edaa70 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt @@ -20,10 +20,11 @@ class TextPreference( titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", icon: String? = null, + iconBold: String? = null, layout: String? = null, tag: String = "app.revanced.extension.shared.settings.preference.ResettableEditTextPreference", val inputType: InputType = InputType.TEXT -) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { +) : BasePreference(key, titleKey, summaryKey, icon, iconBold, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt index 5ea7a0b717..894a51fb7b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt @@ -3,7 +3,9 @@ package app.revanced.patches.shared.misc.spoof import app.revanced.patcher.fingerprint import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method @@ -40,6 +42,9 @@ internal val buildPlayerRequestURIFingerprint = fingerprint { internal val buildRequestFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("Lorg/chromium/net/UrlRequest") // UrlRequest; or UrlRequest$Builder; + instructions( + methodCall(name = "newUrlRequestBuilder") + ) // UrlRequest; or UrlRequest$Builder; custom { methodDef, _ -> // Different targets have slightly different parameters @@ -93,7 +98,6 @@ internal val protobufClassParseByteBufferFingerprint = fingerprint { internal val createStreamingDataFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") parameters("L") opcodes( Opcode.IPUT_OBJECT, @@ -111,7 +115,6 @@ internal val createStreamingDataFingerprint = fingerprint { internal val buildMediaDataSourceFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") parameters( "Landroid/net/Uri;", "J", @@ -126,14 +129,12 @@ internal val buildMediaDataSourceFingerprint = fingerprint { ) } -internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L - internal val hlsCurrentTimeFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters("Z", "L") - literal { - HLS_CURRENT_TIME_FEATURE_FLAG - } + instructions( + literal(45355374L) // HLS current time feature flag. + ) } internal const val DISABLED_BY_SABR_STREAMING_URI_STRING = "DISABLED_BY_SABR_STREAMING_URI" @@ -151,14 +152,16 @@ internal val nerdsStatsVideoFormatBuilderFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("Ljava/lang/String;") parameters("L") - strings("codecs=\"") + instructions( + string("codecs=\"") + ) } internal val patchIncludedExtensionMethodFingerprint = fingerprint { returns("Z") parameters() custom { method, classDef -> - classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded" + method.name == "isPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR } } @@ -166,30 +169,30 @@ internal val patchIncludedExtensionMethodFingerprint = fingerprint { // This code appears to replace the player config after the streams are loaded. // Flag is present in YouTube 19.34, but is missing Platypus stream replacement code until 19.43. // Flag and Platypus code is also present in newer versions of YouTube Music. -internal const val MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG = 45645570L - internal val mediaFetchHotConfigFingerprint = fingerprint { - literal { MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG } + instructions( + literal(45645570L) + ) } // YT 20.10+, YT Music 8.11 - 8.14. // Flag is missing in YT Music 8.15+, and it is not known if a replacement flag/feature exists. -internal const val MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG = 45683169L - internal val mediaFetchHotConfigAlternativeFingerprint = fingerprint { - literal { MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG } + instructions( + literal(45683169L) + ) } // Feature flag that enables different code for parsing and starting video playback, // but it's exact purpose is not known. If this flag is enabled while stream spoofing // then videos will never start playback and load forever. // Flag does not seem to affect playback if spoofing is off. -internal const val PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG = 45665455L - internal val playbackStartDescriptorFeatureFlagFingerprint = fingerprint { parameters() returns("Z") - literal { PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG } + instructions( + literal(45665455L) + ) } internal fun indexOfNewUrlRequestBuilderInstruction(method: Method) = method.indexOfFirstInstruction { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt index 7395c3983d..6d95c76b27 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt @@ -61,6 +61,8 @@ internal fun spoofVideoStreamsPatch( "invoke-static { }, $extensionClassDescriptor->setClientOrderToUse()V" ) + // TODO?: Force off 45708738L ? + // region Enable extension helper method used by other patches patchIncludedExtensionMethodFingerprint.method.returnEarly(true) @@ -69,34 +71,39 @@ internal fun spoofVideoStreamsPatch( // region Block /initplayback requests to fall back to /get_watch requests. - buildInitPlaybackRequestFingerprint.method.apply { - val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex - val targetRegister = getInstruction(moveUriStringIndex).registerA - addInstructions( - moveUriStringIndex + 1, - """ - invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$targetRegister - """ - ) + buildInitPlaybackRequestFingerprint.let { + it.method.apply { + val moveUriStringIndex = it.instructionMatches.first().index + val targetRegister = getInstruction(moveUriStringIndex).registerA + + addInstructions( + moveUriStringIndex + 1, + """ + invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$targetRegister + """ + ) + } } // endregion // region Block /get_watch requests to fall back to /player requests. - buildPlayerRequestURIFingerprint.method.apply { - val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex - val uriRegister = getInstruction(invokeToStringIndex).registerC + buildPlayerRequestURIFingerprint.let { + it.method.apply { + val invokeToStringIndex = it.instructionMatches.first().index + val uriRegister = getInstruction(invokeToStringIndex).registerC - addInstructions( - invokeToStringIndex, - """ - invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; - move-result-object v$uriRegister - """ - ) + addInstructions( + invokeToStringIndex, + """ + invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; + move-result-object v$uriRegister + """ + ) + } } // endregion @@ -106,7 +113,7 @@ internal fun spoofVideoStreamsPatch( buildRequestFingerprint.method.apply { buildRequestMethod = this - val newRequestBuilderIndex = indexOfNewUrlRequestBuilderInstruction(this) + val newRequestBuilderIndex = buildRequestFingerprint.instructionMatches.first().index buildRequestMethodUrlRegister = getInstruction(newRequestBuilderIndex).registerD val freeRegister = findFreeRegister(newRequestBuilderIndex, buildRequestMethodUrlRegister) @@ -126,7 +133,7 @@ internal fun spoofVideoStreamsPatch( createStreamingDataFingerprint.method.apply { val setStreamDataMethodName = "patch_setStreamingData" val resultMethodType = createStreamingDataFingerprint.classDef.type - val videoDetailsIndex = createStreamingDataFingerprint.patternMatch!!.endIndex + val videoDetailsIndex = createStreamingDataFingerprint.instructionMatches.last().index val videoDetailsRegister = getInstruction(videoDetailsIndex).registerA val videoDetailsClass = getInstruction(videoDetailsIndex).getReference()!!.type @@ -137,7 +144,7 @@ internal fun spoofVideoStreamsPatch( ) val protobufClass = protobufClassParseByteBufferFingerprint.method.definingClass - val setStreamingDataIndex = createStreamingDataFingerprint.patternMatch!!.startIndex + val setStreamingDataIndex = createStreamingDataFingerprint.instructionMatches.first().index val playerProtoClass = getInstruction(setStreamingDataIndex + 1) .getReference()!!.definingClass @@ -191,9 +198,9 @@ internal fun spoofVideoStreamsPatch( :disabled return-void - """ + """, ) - } + }, ) } @@ -227,17 +234,17 @@ internal fun spoofVideoStreamsPatch( addInstructions( targetIndex, """ - # Field a: Stream uri. - # Field c: Http method. - # Field d: Post data. - move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register. - iget-object v1, v0, $definingClass->a:Landroid/net/Uri; - iget v2, v0, $definingClass->c:I - iget-object v3, v0, $definingClass->d:[B - invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B - move-result-object v1 - iput-object v1, v0, $definingClass->d:[B - """ + # Field a: Stream uri. + # Field c: Http method. + # Field d: Post data. + move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register. + iget-object v1, v0, $definingClass->a:Landroid/net/Uri; + iget v2, v0, $definingClass->c:I + iget-object v3, v0, $definingClass->d:[B + invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B + move-result-object v1 + iput-object v1, v0, $definingClass->d:[B + """, ) } @@ -263,10 +270,12 @@ internal fun spoofVideoStreamsPatch( // region Fix iOS livestream current time. - hlsCurrentTimeFingerprint.method.insertLiteralOverride( - HLS_CURRENT_TIME_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z" - ) + hlsCurrentTimeFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z" + ) + } // endregion @@ -290,7 +299,7 @@ internal fun spoofVideoStreamsPatch( ) } - fingerprint { + val sabrFingerprint = fingerprint { returns(mediaFetchEnumClass) opcodes( Opcode.SGET_OBJECT, @@ -299,7 +308,8 @@ internal fun spoofVideoStreamsPatch( custom { method, _ -> !method.parameterTypes.isEmpty() } - }.method.addInstructionsWithLabels( + } + sabrFingerprint.method.addInstructionsWithLabels( 0, """ invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableSABR()Z @@ -317,24 +327,30 @@ internal fun spoofVideoStreamsPatch( // region turn off stream config replacement feature flag. if (fixMediaFetchHotConfig()) { - mediaFetchHotConfigFingerprint.method.insertLiteralOverride( - MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" - ) + mediaFetchHotConfigFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" + ) + } } if (fixMediaFetchHotConfigAlternative()) { - mediaFetchHotConfigAlternativeFingerprint.method.insertLiteralOverride( - MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" - ) + mediaFetchHotConfigAlternativeFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" + ) + } } if (fixParsePlaybackResponseFeatureFlag()) { - playbackStartDescriptorFeatureFlagFingerprint.method.insertLiteralOverride( - PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z" - ) + playbackStartDescriptorFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z" + ) + } } // endregion diff --git a/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt index 7ef5093015..c75a12b119 100644 --- a/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt @@ -14,7 +14,7 @@ val removeFileSizeLimitPatch = bytecodePatch( execute { onReadyFingerprint.method.apply { - val cmpIndex = onReadyFingerprint.patternMatch!!.startIndex + 1 + val cmpIndex = onReadyFingerprint.instructionMatches.first().index + 1 val cmpResultRegister = getInstruction(cmpIndex).registerA replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0") diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt index 28780ea578..827a48baaf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt @@ -18,7 +18,6 @@ internal val interceptFingerprint = fingerprint { internal val userConsumerPlanConstructorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") parameters( "Ljava/lang/String;", "Z", diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt index 47a54ed600..2d4abc0f25 100644 --- a/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt @@ -54,7 +54,7 @@ val hideAdsPatch = bytecodePatch( // Prevent verification of an HTTP header containing the user's current plan, which would contradict the previous patch. - val conditionIndex = interceptFingerprint.patternMatch!!.endIndex + 1 + val conditionIndex = interceptFingerprint.instructionMatches.last().index + 1 interceptFingerprint.method.addInstruction( conditionIndex, "return-object p1", diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt index 3a50ae407e..ffea10a919 100644 --- a/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt @@ -5,7 +5,6 @@ import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode internal val featureConstructorFingerprint = fingerprint { - returns("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) parameters("Ljava/lang/String;", "Z", "Ljava/util/List;") opcodes( diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/hide/createbutton/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/hide/createbutton/Fingerprints.kt deleted file mode 100644 index d9235b8c80..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/hide/createbutton/Fingerprints.kt +++ /dev/null @@ -1,31 +0,0 @@ -package app.revanced.patches.spotify.layout.hide.createbutton - -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.MethodReference - -@Deprecated("Obsolete") -internal val navigationBarItemSetClassFingerprint = fingerprint { - strings("NavigationBarItemSet(") -} - -@Deprecated("Obsolete") -internal val navigationBarItemSetConstructorFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - // Make sure the method checks whether navigation bar items are null before adding them. - // If this is not true, then we cannot patch the method and potentially transform the parameters into null. - opcodes(Opcode.IF_EQZ, Opcode.INVOKE_VIRTUAL) - custom { method, _ -> - method.indexOfFirstInstruction { - getReference()?.name == "add" - } >= 0 - } -} - -@Deprecated("Obsolete") -internal val oldNavigationBarAddItemFingerprint = fingerprint { - strings("Bottom navigation tabs exceeds maximum of 5 tabs") -} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/hide/createbutton/HideCreateButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/hide/createbutton/HideCreateButtonPatch.kt deleted file mode 100644 index 465f18d690..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/hide/createbutton/HideCreateButtonPatch.kt +++ /dev/null @@ -1,110 +0,0 @@ -package app.revanced.patches.spotify.layout.hide.createbutton - -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference - -private const val EXTENSION_CLASS_DESCRIPTOR = - "Lapp/revanced/extension/spotify/layout/hide/createbutton/HideCreateButtonPatch;" - -@Deprecated("Patch no longer works with the latest version of Spotify, " + - "and Spotify has added this functionality to the app") -@Suppress("unused") -val hideCreateButtonPatch = bytecodePatch( - description = "Hides the \"Create\" button in the navigation bar. The latest app targets do not need this patch.", -) { - compatibleWith("com.spotify.music") - - dependsOn(sharedExtensionPatch) - - execute { - val oldNavigationBarAddItemMethod = oldNavigationBarAddItemFingerprint.originalMethodOrNull - // Only throw the fingerprint error when oldNavigationBarAddItemMethod does not exist. - val navigationBarItemSetClassDef = if (oldNavigationBarAddItemMethod == null) { - navigationBarItemSetClassFingerprint.originalClassDef - } else { - navigationBarItemSetClassFingerprint.originalClassDefOrNull - } - - if (navigationBarItemSetClassDef != null) { - // Main patch for newest and most versions. - // The NavigationBarItemSet constructor accepts multiple parameters which represent each navigation bar item. - // Each item is manually checked whether it is not null and then added to a LinkedHashSet. - // Since the order of the items can differ, we are required to check every parameter to see whether it is the - // Create button. So, for every parameter passed to the method, invoke our extension method and overwrite it - // to null in case it is the Create button. - navigationBarItemSetConstructorFingerprint.match(navigationBarItemSetClassDef).method.apply { - // Add 1 to the index because the first parameter register is `this`. - val parameterTypesWithRegister = parameterTypes.mapIndexed { index, parameterType -> - parameterType to (index + 1) - } - - val returnNullIfIsCreateButtonDescriptor = - "$EXTENSION_CLASS_DESCRIPTOR->returnNullIfIsCreateButton(Ljava/lang/Object;)Ljava/lang/Object;" - - parameterTypesWithRegister.reversed().forEach { (parameterType, parameterRegister) -> - addInstructions( - 0, - """ - invoke-static { p$parameterRegister }, $returnNullIfIsCreateButtonDescriptor - move-result-object p$parameterRegister - check-cast p$parameterRegister, $parameterType - """ - ) - } - } - } - - if (oldNavigationBarAddItemMethod != null) { - // In case an older version of the app is being patched, hook the old method which adds navigation bar items. - // Return early if the navigation bar item title resource id is the old Create button title resource id. - oldNavigationBarAddItemFingerprint.methodOrNull?.apply { - val getNavigationBarItemTitleStringIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.definingClass == "Landroid/content/res/Resources;" && reference.name == "getString" - } - // This register is a parameter register, so it can be used at the start of the method when adding - // the new instructions. - val oldNavigationBarItemTitleResIdRegister = - getInstruction(getNavigationBarItemTitleStringIndex).registerD - - // The instruction where the normal method logic starts. - val firstInstruction = getInstruction(0) - - val isOldCreateButtonDescriptor = - "$EXTENSION_CLASS_DESCRIPTOR->isOldCreateButton(I)Z" - - val returnEarlyInstruction = if (returnType == "V") { - // In older implementations the method return value is void. - "return-void" - } else { - // In newer implementations - // return null because the method return value is a BottomNavigationItemView. - "const/4 v0, 0\n" + - "return-object v0" - } - - addInstructionsWithLabels( - 0, - """ - invoke-static { v$oldNavigationBarItemTitleResIdRegister }, $isOldCreateButtonDescriptor - move-result v0 - - # If this navigation bar item is not the Create button, jump to the normal method logic. - if-eqz v0, :normal-method-logic - - $returnEarlyInstruction - """, - ExternalLabel("normal-method-logic", firstInstruction) - ) - } - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt deleted file mode 100644 index 4d2d04a553..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.spotify.lite.ondemand - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Patch no longer works and will be deleted soon") -@Suppress("unused") -val onDemandPatch = bytecodePatch( - description = "Enables listening to songs on-demand, allowing to play any song from playlists, albums or artists without limitations. This does not remove ads.", -) diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/ExtensionPatch.kt index 070190a037..2736a0e373 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/ExtensionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/ExtensionPatch.kt @@ -3,7 +3,7 @@ package app.revanced.patches.spotify.misc.extension import app.revanced.patches.shared.misc.extension.sharedExtensionPatch val sharedExtensionPatch = sharedExtensionPatch( - "spotify", + "spotify", mainActivityOnCreateHook, loadOrbitLibraryHook ) diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Hooks.kt index 4bddc43b8a..9eca1b7d1b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Hooks.kt @@ -8,19 +8,23 @@ import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference -internal val mainActivityOnCreateHook = extensionHook(fingerprint = mainActivityOnCreateFingerprint) +internal val mainActivityOnCreateHook = extensionHook { mainActivityOnCreateFingerprint } -internal val loadOrbitLibraryHook = extensionHook( - insertIndexResolver = { - loadOrbitLibraryFingerprint.stringMatches!!.last().index - }, - contextRegisterResolver = { method -> - val contextReferenceIndex = method.indexOfFirstInstruction { - getReference()?.type == "Landroid/content/Context;" - } - val contextRegister = method.getInstruction(contextReferenceIndex).registerA +internal val loadOrbitLibraryHook = extensionHook { + // FIXME: Creating this is a mess and needs refactoring. + extensionHook( + insertIndexResolver = { + loadOrbitLibraryFingerprint.stringMatches.last().index + }, + contextRegisterResolver = { method -> + val contextReferenceIndex = method.indexOfFirstInstruction { + getReference()?.type == "Landroid/content/Context;" + } + val contextRegister = + method.getInstruction(contextReferenceIndex).registerA - "v$contextRegister" - }, - fingerprint = loadOrbitLibraryFingerprint, -) + "v$contextRegister" + }, + fingerprint = loadOrbitLibraryFingerprint, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/Fingerprints.kt deleted file mode 100644 index 3be19dcce8..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/Fingerprints.kt +++ /dev/null @@ -1,52 +0,0 @@ -package app.revanced.patches.spotify.misc.fix - -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.MethodReference - -internal val loadOrbitLibraryFingerprint = fingerprint { - strings("/liborbit-jni-spotify.so") -} - -internal val setClientIdFingerprint = fingerprint { - parameters("Ljava/lang/String;") - custom { method, classDef -> - classDef.type == "Lcom/spotify/connectivity/ApplicationScopeConfiguration;" - && method.name == "setClientId" - } -} - -internal val setUserAgentFingerprint = fingerprint { - parameters("Ljava/lang/String;") - custom { method, classDef -> - classDef.type == "Lcom/spotify/connectivity/ApplicationScopeConfiguration;" - && method.name == "setDefaultHTTPUserAgent" - } -} - -internal val extensionFixConstantsFingerprint = fingerprint { - custom { _, classDef -> classDef.type == "Lapp/revanced/extension/spotify/misc/fix/Constants;" } -} - -internal val runIntegrityVerificationFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - opcodes( - Opcode.CHECK_CAST, - Opcode.INVOKE_VIRTUAL, - Opcode.INVOKE_STATIC, // Calendar.getInstance() - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, // instance.get(6) - Opcode.MOVE_RESULT, - Opcode.IF_EQ, // if (x == instance.get(6)) return - ) - custom { method, _ -> - method.indexOfFirstInstruction { - val reference = getReference() - reference?.definingClass == "Ljava/util/Calendar;" && reference.name == "get" - } >= 0 - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofClientPatch.kt deleted file mode 100644 index d57370b3f4..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofClientPatch.kt +++ /dev/null @@ -1,125 +0,0 @@ -package app.revanced.patches.spotify.misc.fix - -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.intOption -import app.revanced.patcher.patch.stringOption -import app.revanced.patches.shared.misc.hex.HexPatchBuilder -import app.revanced.patches.shared.misc.hex.hexPatch -import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch -import app.revanced.util.returnEarly - -internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/fix/SpoofClientPatch;" - -@Deprecated("Patch no longer functions") -@Suppress("unused") -val spoofClientPatch = bytecodePatch( - description = "Spoofs the client to fix various functions of the app.", -) { - val requestListenerPort by intOption( - key = "requestListenerPort", - default = 4345, - title = "Request listener port", - description = "The port to use for the listener that intercepts and handles spoofed requests. " + - "Port must be between 0 and 65535. " + - "Do not change this option, if you do not know what you are doing.", - validator = { - it!! - !(it < 0 || it > 65535) - } - ) - - val clientVersion by stringOption( - key = "clientVersion", - default = "iphone-9.0.58.558.g200011c", - title = "Client version", - description = "The client version used for spoofing the client token. " + - "Do not change this option, if you do not know what you are doing." - ) - - val hardwareMachine by stringOption( - key = "hardwareMachine", - default = "iPhone16,1", - title = "Hardware machine", - description = "The hardware machine used for spoofing the client token. " + - "Do not change this option, if you do not know what you are doing." - ) - - val systemVersion by stringOption( - key = "systemVersion", - default = "17.7.2", - title = "System version", - description = "The system version used for spoofing the client token. " + - "Do not change this option, if you do not know what you are doing." - ) - - dependsOn( - sharedExtensionPatch, - hexPatch(ignoreMissingTargetFiles = true, block = fun HexPatchBuilder.() { - listOf( - "arm64-v8a", - "armeabi-v7a", - "x86", - "x86_64" - ).forEach { architecture -> - "https://clienttoken.spotify.com/v1/clienttoken" to - "http://127.0.0.1:$requestListenerPort/v1/clienttoken" inFile - "lib/$architecture/liborbit-jni-spotify.so" - } - }) - ) - - compatibleWith("com.spotify.music") - - execute { - val clientVersion = clientVersion!! - val hardwareMachine = hardwareMachine!! - val systemVersion = systemVersion!! - - // region Spoof login request. - - val version = clientVersion - .substringAfter('-') - .substringBeforeLast('.') - .substringBeforeLast('.') - - setUserAgentFingerprint.method.addInstruction( - 0, - "const-string p1, \"Spotify/$version iOS/$systemVersion ($hardwareMachine)\"" - ) - - setClientIdFingerprint.method.addInstruction( - 0, "const-string p1, \"58bd3c95768941ea9eb4350aaa033eb3\"" - ) - - // endregion - - // region Spoof client-token request. - - loadOrbitLibraryFingerprint.method.addInstructions( - 0, - """ - const/16 v0, $requestListenerPort - invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->launchListener(I)V - """ - ) - - mapOf( - "getClientVersion" to clientVersion, - "getSystemVersion" to systemVersion, - "getHardwareMachine" to hardwareMachine - ).forEach { (methodName, value) -> - extensionFixConstantsFingerprint.classDef.methods.single { it.name == methodName }.returnEarly(value) - } - - // endregion - - // region Disable verdicts. - - // Early return to block sending bad verdicts to the API. - runIntegrityVerificationFingerprint.method.returnEarly() - - // endregion - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatch.kt deleted file mode 100644 index 20f9b3b4e5..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatch.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.spotify.misc.fix - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Superseded by spoofClientPatch", ReplaceWith("spoofClientPatch")) -@Suppress("unused") -val spoofPackageInfoPatch = bytecodePatch( - description = "Spoofs the package info of the app to fix various functions of the app.", -) { - dependsOn(spoofClientPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofSignaturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofSignaturePatch.kt deleted file mode 100644 index 238da0f41e..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofSignaturePatch.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.spotify.misc.fix - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Superseded by spoofClientPatch", ReplaceWith("spoofClientPatch")) -@Suppress("unused") -val spoofSignaturePatch = bytecodePatch( - description = "Spoofs the signature of the app fix various functions of the app.", -) { - dependsOn(spoofClientPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/Fingerprints.kt index b9edaaeacf..be6b4165a1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/Fingerprints.kt @@ -1,6 +1,7 @@ package app.revanced.patches.spotify.misc.fix.login import app.revanced.patcher.fingerprint +import app.revanced.patcher.literal import app.revanced.util.literal internal val katanaProxyLoginMethodHandlerClassFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/ChangeLyricsProviderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/ChangeLyricsProviderPatch.kt index 16253bd38b..2b6a5cf587 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/ChangeLyricsProviderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/ChangeLyricsProviderPatch.kt @@ -3,10 +3,12 @@ package app.revanced.patches.spotify.misc.lyrics import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.fingerprint import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.stringOption import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c @@ -91,7 +93,18 @@ val changeLyricsProviderPatch = bytecodePatch( // region Replace the call to the HTTP client builder method used exclusively for lyrics by the modified one. - getLyricsHttpClientFingerprint(httpClientBuilderMethod).method.apply { + + val getLyricsHttpClientFingerprint = fingerprint { + returns(httpClientBuilderMethod.returnType) + parameters() + custom { method, _ -> + method.indexOfFirstInstruction { + getReference() == httpClientBuilderMethod + } >= 0 + } + } + + getLyricsHttpClientFingerprint.method.apply { val getLyricsHttpClientIndex = indexOfFirstInstructionOrThrow { getReference() == httpClientBuilderMethod } diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt deleted file mode 100644 index bcf94324c8..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.spotify.navbar - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Obsolete and will be deleted soon") -@Suppress("unused") -val premiumNavbarTabPatch = bytecodePatch( - description = "Hides the premium tab from the navigation bar.", -) diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt index e59660472d..d0e061b9c6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt @@ -12,7 +12,7 @@ val unlockSubscriptionPatch = bytecodePatch( execute { getSubscribedFingerprint.method.replaceInstruction( - getSubscribedFingerprint.patternMatch!!.startIndex, + getSubscribedFingerprint.instructionMatches.first().index, "const/4 v0, 0x1", ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt index c91bb961a2..69fcc44d41 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt @@ -52,7 +52,7 @@ val disableSubscriptionSuggestionsPatch = bytecodePatch( }, ) - val getModulesIndex = getModulesFingerprint.patternMatch!!.startIndex + val getModulesIndex = getModulesFingerprint.instructionMatches.first().index with(originalMethod) { removeInstruction(getModulesIndex) addInstructions( diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt index db1912677b..9b8a213b0c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt @@ -1,14 +1,11 @@ package app.revanced.patches.tiktok.misc.extension +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook import app.revanced.patches.shared.misc.extension.extensionHook -import com.android.tools.smali.dexlib2.AccessFlags -internal val initHook = extensionHook { - custom { method, classDef -> - classDef.type == "Lcom/ss/android/ugc/aweme/main/MainActivity;" && - method.name == "onCreate" - } -} +internal val initHook = activityOnCreateExtensionHook( + "Lcom/ss/android/ugc/aweme/main/MainActivity;" +) /** * In some cases the extension code can be called before diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt index 40a45650de..b069b88f55 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt @@ -69,7 +69,7 @@ val spoofSimPatch = bytecodePatch( } } }.forEach { (classDef, methods) -> - with(proxy(classDef).mutableClass) { + with(mutableClassBy(classDef)) { methods.forEach { (method, patches) -> with(findMutableMethodOf(method)) { while (!patches.isEmpty()) { diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt index c2da658aad..06f7d44379 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt @@ -69,7 +69,7 @@ val overrideFeatureFlagsPatch = bytecodePatch( // This is equivalent to // String forcedValue = getValueOverride(feature) // if (forcedValue != null) return forcedValue - val getFeatureIndex = getFeatureValueFingerprint.patternMatch!!.startIndex + val getFeatureIndex = getFeatureValueFingerprint.instructionMatches.first().index getFeatureValueFingerprint.method.addInstructionsWithLabels( getFeatureIndex, """ diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt index dbe75d5f8b..ad9f52f4b0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt @@ -21,7 +21,7 @@ val fixOldVersionsPatch = bytecodePatch( // Remove the live query parameters from the path when it's specified via a @METHOD annotation. for (liveQueryParameter in liveQueryParameters) { httpPathParserFingerprint.method.addInstructions( - httpPathParserFingerprint.patternMatch!!.endIndex + 1, + httpPathParserFingerprint.instructionMatches.last().index + 1, """ # urlPath = urlPath.replace(liveQueryParameter, "") const-string p1, "$liveQueryParameter" diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt index c4ab799e85..5e8ad5c70a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt @@ -24,7 +24,7 @@ val filterTimelineObjectsPatch = bytecodePatch( dependsOn(sharedExtensionPatch) execute { - val filterInsertIndex = timelineFilterExtensionFingerprint.patternMatch!!.startIndex + val filterInsertIndex = timelineFilterExtensionFingerprint.instructionMatches.first().index timelineFilterExtensionFingerprint.method.apply { val addInstruction = getInstruction(filterInsertIndex + 1) @@ -47,7 +47,7 @@ val filterTimelineObjectsPatch = bytecodePatch( } } - mapOf( + arrayOf( timelineConstructorFingerprint to 1, postsResponseConstructorFingerprint to 2, ).forEach { (fingerprint, timelineObjectsRegister) -> diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt index 48ecefba8e..2cf8e55925 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt @@ -29,7 +29,7 @@ fun adPatch( classDefType: String, methodNames: Set, returnMethod: ReturnMethod, - ) = with(classBy { classDefType == it.type }?.mutableClass) { + ) = with(mutableClassByOrNull(classDefType)) { this ?: return false methods.filter { it.name in methodNames }.forEach { diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt index 21da99a5bb..a656e34a54 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt @@ -11,7 +11,6 @@ internal val chatUtilCreateDeletedSpanFingerprint = fingerprint { internal val deletedMessageClickableSpanCtorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") custom { _, classDef -> classDef.endsWith("DeletedMessageClickableSpan;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt index 9a46867ab5..1a345d75cd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt @@ -1,9 +1,5 @@ package app.revanced.patches.twitch.misc.extension -import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook -internal val initHook = extensionHook { - custom { method, classDef -> - method.name == "onCreate" && classDef.endsWith("/TwitchApplication;") - } -} +internal val initHook = activityOnCreateExtensionHook("/TwitchApplication;") diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt index dc100acb10..623926102e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt @@ -16,7 +16,6 @@ internal val buildMediaOptionsSheetFingerprint = fingerprint { internal val constructMediaOptionsSheetFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") strings("captionsState") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt index 6f2b9c12c7..945f0a47b9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt @@ -27,7 +27,7 @@ val unlockDownloadsPatch = bytecodePatch( // Allow downloads for non-premium users. showDownloadVideoUpsellBottomSheetFingerprint.patch { - val checkIndex = patternMatch!!.startIndex + val checkIndex = instructionMatches.first().index val register = method.getInstruction(checkIndex).registerA checkIndex to register @@ -42,25 +42,26 @@ val unlockDownloadsPatch = bytecodePatch( } // Make GIFs downloadable. - val patternMatch = buildMediaOptionsSheetFingerprint.patternMatch!! - buildMediaOptionsSheetFingerprint.method.apply { - val checkMediaTypeIndex = patternMatch.startIndex - val checkMediaTypeInstruction = getInstruction(checkMediaTypeIndex) + buildMediaOptionsSheetFingerprint.let { + it.method.apply { + val checkMediaTypeIndex = it.instructionMatches.first().index + val checkMediaTypeInstruction = getInstruction(checkMediaTypeIndex) - // Treat GIFs as videos. - addInstructionsWithLabels( - checkMediaTypeIndex + 1, - """ + // Treat GIFs as videos. + addInstructionsWithLabels( + checkMediaTypeIndex + 1, + """ const/4 v${checkMediaTypeInstruction.registerB}, 0x2 # GIF if-eq v${checkMediaTypeInstruction.registerA}, v${checkMediaTypeInstruction.registerB}, :video """, - ExternalLabel("video", getInstruction(patternMatch.endIndex)), - ) + ExternalLabel("video", getInstruction(it.instructionMatches.last().index)), + ) - // Remove media.isDownloadable check. - removeInstruction( - instructions.first { it.opcode == Opcode.IGET_BOOLEAN }.location.index + 1, - ) + // Remove media.isDownloadable check. + removeInstruction( + instructions.first { it.opcode == Opcode.IGET_BOOLEAN }.location.index + 1, + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt index 342800518a..74a6136f82 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt @@ -38,8 +38,7 @@ val dynamicColorPatch = resourcePatch( } document("res/values-v31/colors.xml").use { document -> - - mapOf( + arrayOf( "ps__twitter_blue" to "@color/twitter_blue", "ps__twitter_blue_pressed" to "@color/twitter_blue_fill_pressed", "twitter_blue" to "@android:color/system_accent1_400", @@ -59,7 +58,7 @@ val dynamicColorPatch = resourcePatch( } document("res/values-night-v31/colors.xml").use { document -> - mapOf( + arrayOf( "twitter_blue" to "@android:color/system_accent1_200", "twitter_blue_fill_pressed" to "@android:color/system_accent1_300", "twitter_blue_opacity_30" to "@android:color/system_accent1_50", diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt index 56785cae4c..d3e95102fa 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt @@ -22,7 +22,7 @@ fun addJsonHook( jsonHookPatchFingerprint.method.apply { // Insert hooks right before calling buildList. - val insertIndex = jsonHookPatchFingerprint.patternMatch!!.endIndex + val insertIndex = jsonHookPatchFingerprint.instructionMatches.last().index addInstructions( insertIndex, @@ -48,11 +48,9 @@ val jsonHookPatch = bytecodePatch( execute { jsonHookPatchFingerprint.apply { - // Make sure the extension is present. - val jsonHookPatch = classBy { classDef -> classDef.type == JSON_HOOK_PATCH_CLASS_DESCRIPTOR } - ?: throw PatchException("Could not find the extension.") + val jsonHookPatch = classBy(JSON_HOOK_PATCH_CLASS_DESCRIPTOR) - matchOrNull(jsonHookPatch.immutableClass) + matchOrNull(jsonHookPatch) ?: throw PatchException("Unexpected extension.") } @@ -61,7 +59,7 @@ val jsonHookPatch = bytecodePatch( .fields .firstOrNull { it.name == "JSON_FACTORY" } ?.type - .let { type -> classes.find { it.type == type } } + ?.let { type -> classes.classBy(type) } ?: throw PatchException("Could not find required class.") // Hook the methods first parameter. @@ -77,7 +75,7 @@ val jsonHookPatch = bytecodePatch( finalize { // Remove hooks.add(dummyHook). jsonHookPatchFingerprint.method.apply { - val addDummyHookIndex = jsonHookPatchFingerprint.patternMatch!!.endIndex - 2 + val addDummyHookIndex = jsonHookPatchFingerprint.instructionMatches.last().index - 2 removeInstructions(addDummyHookIndex, 2) } @@ -99,8 +97,8 @@ class JsonHook( internal var added = false init { - classBy { it.type == descriptor }?.let { - it.mutableClass.also { classDef -> + mutableClassBy(descriptor).let { + it.also { classDef -> if ( classDef.superclass != JSON_HOOK_CLASS_DESCRIPTOR || !classDef.fields.any { field -> field.name == "INSTANCE" } @@ -108,6 +106,6 @@ class JsonHook( throw InvalidClassException(classDef.type, "Not a hook class") } } - } ?: throw ClassNotFoundException("Failed to find hook class $descriptor") + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt index 6117798ebf..90e4a16f4b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt @@ -3,11 +3,6 @@ package app.revanced.patches.twitter.misc.links import app.revanced.patcher.fingerprint import com.android.tools.smali.dexlib2.AccessFlags -internal val openLinkFingerprint = fingerprint { - returns("V") - parameters("Landroid/content/Context;", "Landroid/content/Intent;", "Landroid/os/Bundle;") -} - internal val sanitizeSharingLinksFingerprint = fingerprint { returns("Ljava/lang/String;") strings("", "shareParam", "sessionToken") diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt deleted file mode 100644 index 9c109e3602..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt +++ /dev/null @@ -1,31 +0,0 @@ -package app.revanced.patches.twitter.misc.links - -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.twitter.misc.extension.sharedExtensionPatch - -@Deprecated("Patch is obsolete and no longer needed with the highest supported app target. " + - "This patch will soon be deleted.") -@Suppress("unused") -val openLinksWithAppChooserPatch = bytecodePatch( - description = "Instead of opening links directly, open them with an app chooser. " + - "As a result you can select a browser to open the link with.", -) { - dependsOn(sharedExtensionPatch) - - compatibleWith("com.twitter.android"("10.48.0-release.0")) - - execute { - val methodReference = - "Lapp/revanced/extension/twitter/patches/links/OpenLinksWithAppChooserPatch;->" + - "openWithChooser(Landroid/content/Context;Landroid/content/Intent;)V" - - openLinkFingerprint.method.addInstructions( - 0, - """ - invoke-static { p0, p1 }, $methodReference - return-void - """, - ) - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/viber/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/viber/ads/HideAdsPatch.kt index 0ec31e376f..b9e3b64ce4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/viber/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/viber/ads/HideAdsPatch.kt @@ -2,7 +2,6 @@ package app.revanced.patches.viber.ads import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.fingerprint -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.returnEarly @@ -30,12 +29,13 @@ val hideAdsPatch = bytecodePatch( val targetClass = method.getInstruction(typeRefIndex).reference as TypeReference // Patch the ads-free method to always return true - fingerprint { + val adFreeFingerprint = fingerprint { returns("I") parameters() custom { method, classDef -> classDef == targetClass } - }.method.returnEarly(1) + } + adFreeFingerprint.method.returnEarly(1) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/HideNavigationButtons.kt b/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/HideNavigationButtons.kt index 2c07b2b10a..c439810b8b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/HideNavigationButtons.kt +++ b/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/HideNavigationButtons.kt @@ -5,7 +5,6 @@ import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.shared.PATCH_NAME_HIDE_NAVIGATION_BUTTONS import java.util.logging.Logger -import kotlin.collections.joinToString @Suppress("unused") val hideNavigationButtonsPatch = bytecodePatch( @@ -46,8 +45,7 @@ val hideNavigationButtonsPatch = bytecodePatch( nop """ - shouldShowTabIdMethodFingerprint - .method + shouldShowTabIdMethodFingerprint.method .addInstructionsWithLabels(0, injectionInstructions) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/Fingerprints.kt deleted file mode 100644 index 0809324c7f..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/Fingerprints.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.vsco.misc.pro - -import app.revanced.patcher.fingerprint - -internal val revCatSubscriptionFingerprint = fingerprint { - returns("V") - strings("use_debug_subscription_settings") - custom { _, classDef -> - classDef.endsWith("/RevCatSubscriptionSettingsRepository;") - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt deleted file mode 100644 index be0278dd9e..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt +++ /dev/null @@ -1,17 +0,0 @@ -package app.revanced.patches.vsco.misc.pro - -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("This patch is deprecated because it does not work anymore and will be removed in the future.") -@Suppress("unused") -val unlockProPatch = bytecodePatch( - description = "Unlocks pro features.", -) { - compatibleWith("com.vsco.cam"("345")) - - execute { - // Set isSubscribed to true. - revCatSubscriptionFingerprint.method.addInstruction(0, "const p1, 0x1") - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt index b3dd1a3fcf..0d7119a0b2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt @@ -11,7 +11,7 @@ internal val hideAdsPatch = bytecodePatch( compatibleWith("at.willhaben") execute { - adResolverFingerprint.method.returnEarly() + adResolverFingerprint.method.returnEarly(null) whAdViewInjectorFingerprint.method.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/Fingerprints.kt deleted file mode 100644 index f199f127c7..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/Fingerprints.kt +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.patches.windyapp.misc.unlockpro - -import app.revanced.patcher.fingerprint - -internal val checkProFingerprint = fingerprint { - returns("I") - custom { method, classDef -> - classDef.endsWith("RawUserData;") && method.name == "isPro" - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt deleted file mode 100644 index c0323c72ed..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt +++ /dev/null @@ -1,22 +0,0 @@ -package app.revanced.patches.windyapp.misc.unlockpro - -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("This patch no longer works and will be removed in the future.") -@Suppress("unused") -val unlockProPatch = bytecodePatch( - description = "Unlocks all pro features.", -) { - compatibleWith("co.windyapp.android") - - execute { - checkProFingerprint.method.addInstructions( - 0, - """ - const/16 v0, 0x1 - return v0 - """, - ) - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt index d71664b80a..c47aa69bca 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt @@ -2,14 +2,15 @@ package app.revanced.patches.youtube.ad.general import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.opcode import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.fix.verticalscroll.verticalScrollPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch @@ -23,6 +24,7 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c +import org.stringtemplate.v4.compiler.Bytecode.instructions internal var adAttributionId = -1L private set @@ -57,8 +59,8 @@ private val hideAdsResourcePatch = resourcePatch { addLithoFilter("Lapp/revanced/extension/youtube/patches/components/AdsFilter;") - adAttributionId = resourceMappings["id", "ad_attribution"] - fullScreenEngagementAdContainer = resourceMappings["id", "fullscreen_engagement_ad_container"] + adAttributionId = getResourceId(ResourceType.ID, "ad_attribution") + fullScreenEngagementAdContainer = getResourceId(ResourceType.ID, "fullscreen_engagement_ad_container") } } @@ -76,10 +78,10 @@ val hideAdsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -123,8 +125,7 @@ val hideAdsPatch = bytecodePatch( // Hide the view val viewRegister = (this as Instruction35c).registerC - proxy(classDef) - .mutableClass + mutableClassBy(classDef) .findMutableMethodOf(method) .injectHideViewCall( insertIndex, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt index 1ae278e615..fc669feedd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt @@ -25,10 +25,10 @@ val hideGetPremiumPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -40,7 +40,7 @@ val hideGetPremiumPatch = bytecodePatch( ) getPremiumViewFingerprint.method.apply { - val startIndex = getPremiumViewFingerprint.patternMatch!!.startIndex + val startIndex = getPremiumViewFingerprint.instructionMatches.first().index val measuredWidthRegister = getInstruction(startIndex).registerA val measuredHeightInstruction = getInstruction(startIndex + 1) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt index 91cc0e8dfa..de155fbb29 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt @@ -5,7 +5,6 @@ import app.revanced.patcher.fingerprint internal val loadVideoAdsFingerprint = fingerprint { strings( "TriggerBundle doesn't have the required metadata specified by the trigger ", - "Tried to enter slot with no assigned slotAdapter", - "Trying to enter a slot when a slot of same type and physical position is already active. Its status: ", + "Ping migration no associated ping bindings for activated trigger: ", ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt index 709bb7c4b6..d47c72239e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt @@ -23,10 +23,10 @@ val videoAdsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt index 1109d0fc86..b7f6352d1b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt @@ -15,7 +15,7 @@ import app.revanced.util.copyResources private val copyVideoUrlResourcePatch = resourcePatch { dependsOn( settingsPatch, - playerControlsResourcePatch, + playerControlsPatch, addResourcesPatch, ) @@ -53,10 +53,10 @@ val copyVideoUrlPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt index b252875901..ad44f3ca3a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt @@ -1,22 +1,15 @@ package app.revanced.patches.youtube.interaction.dialog -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.AccessFlags import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall internal val createDialogFingerprint = fingerprint { - accessFlags(AccessFlags.PROTECTED) returns("V") parameters("L", "L", "Ljava/lang/String;") - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IPUT_OBJECT, - Opcode.IGET_OBJECT, - Opcode.INVOKE_VIRTUAL, // dialog.show() + instructions( + methodCall(smali = "Landroid/app/AlertDialog\$Builder;->setNegativeButton(ILandroid/content/DialogInterface\$OnClickListener;)Landroid/app/AlertDialog\$Builder;"), + methodCall(smali = "Landroid/app/AlertDialog\$Builder;->setOnCancelListener(Landroid/content/DialogInterface\$OnCancelListener;)Landroid/app/AlertDialog\$Builder;"), + methodCall(smali = "Landroid/app/AlertDialog\$Builder;->create()Landroid/app/AlertDialog;"), + methodCall(smali = "Landroid/app/AlertDialog;->show()V") ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt index d4030d6bfd..fd72226317 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt @@ -11,6 +11,8 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch;" + val removeViewerDiscretionDialogPatch = bytecodePatch( name = "Remove viewer discretion dialog", description = "Adds an option to remove the dialog that appears when opening a video that has been age-restricted " + @@ -24,17 +26,13 @@ val removeViewerDiscretionDialogPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) - val extensionMethodDescriptor = - "Lapp/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch;->" + - "confirmDialog(Landroid/app/AlertDialog;)V" - execute { addResources("youtube", "interaction.dialog.removeViewerDiscretionDialogPatch") @@ -42,14 +40,16 @@ val removeViewerDiscretionDialogPatch = bytecodePatch( SwitchPreference("revanced_remove_viewer_discretion_dialog"), ) - createDialogFingerprint.method.apply { - val showDialogIndex = implementation!!.instructions.lastIndex - 2 - val dialogRegister = getInstruction(showDialogIndex).registerC + createDialogFingerprint.let { + it.method.apply { + val showDialogIndex = it.instructionMatches.last().index + val dialogRegister = getInstruction(showDialogIndex).registerC - replaceInstructions( - showDialogIndex, - "invoke-static { v$dialogRegister }, $extensionMethodDescriptor", - ) + replaceInstructions( + showDialogIndex, + "invoke-static { v$dialogRegister }, $EXTENSION_CLASS_DESCRIPTOR->confirmDialog(Landroid/app/AlertDialog;)V", + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/DisableChapterSkipDoubleTapPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/DisableChapterSkipDoubleTapPatch.kt index fbf1535070..6361c383f8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/DisableChapterSkipDoubleTapPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/DisableChapterSkipDoubleTapPatch.kt @@ -7,10 +7,13 @@ import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import java.util.logging.Logger private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisableDoubleTapActionsPatch;" @@ -24,17 +27,27 @@ val disableDoubleTapActionsPatch = bytecodePatch( sharedExtensionPatch, settingsPatch, addResourcesPatch, + versionCheckPatch ) compatibleWith( "com.google.android.youtube"( - "20.07.39", - "20.13.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) execute { + if (!is_20_14_or_greater) { + // Show a message if users have version constrain off and are patching the oldest version, + // just to prevent spamming a cryptic error message the user may not understand + // and don't add in app settings that won't work. + return@execute Logger.getLogger(this::class.java.name).warning( + "Disable double tap actions requires 20.14.43+" + ) + } + addResources("youtube", "interaction.doubletap.disableDoubleTapActionsPatch") PreferenceScreen.PLAYER.addPreferences( @@ -77,8 +90,3 @@ val disableDoubleTapActionsPatch = bytecodePatch( ) } } - -@Deprecated("Patch was renamed", ReplaceWith("disableDoubleTapActionsPatch")) -val disableChapterSkipDoubleTapPatch = bytecodePatch { - dependsOn(disableDoubleTapActionsPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt index 8e34a72280..2e2ada37ab 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt @@ -14,7 +14,6 @@ import app.revanced.patches.youtube.misc.playercontrols.addBottomControl import app.revanced.patches.youtube.misc.playercontrols.initializeBottomControl import app.revanced.patches.youtube.misc.playercontrols.injectVisibilityCheckCall import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch -import app.revanced.patches.youtube.misc.playercontrols.playerControlsResourcePatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint @@ -24,7 +23,7 @@ import app.revanced.util.copyResources private val downloadsResourcePatch = resourcePatch { dependsOn( - playerControlsResourcePatch, + playerControlsPatch, settingsPatch, addResourcesPatch, ) @@ -74,10 +73,10 @@ val downloadsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -88,7 +87,7 @@ val downloadsPatch = bytecodePatch( // Main activity is used to launch downloader intent. mainActivityOnCreateFingerprint.method.addInstruction( 0, - "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->activityCreated(Landroid/app/Activity;)V" + "invoke-static/range { p0 .. p0 }, ${EXTENSION_CLASS_DESCRIPTOR}->setMainActivity(Landroid/app/Activity;)V" ) offlineVideoEndpointFingerprint.method.apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt index f10fc8e834..bf31e036be 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt @@ -1,7 +1,8 @@ package app.revanced.patches.youtube.interaction.downloads -import com.android.tools.smali.dexlib2.AccessFlags import app.revanced.patcher.fingerprint +import app.revanced.patcher.string +import com.android.tools.smali.dexlib2.AccessFlags internal val offlineVideoEndpointFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) @@ -12,5 +13,7 @@ internal val offlineVideoEndpointFingerprint = fingerprint { "Ljava/lang/String", // VideoId "L", ) - strings("Object is not an offlineable video: ") + instructions( + string("Object is not an offlineable video: ") + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt index 162b690905..6ee07c8110 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt @@ -11,8 +11,6 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.util.findFreeRegister -import app.revanced.util.indexOfFirstInstructionOrThrow -import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference @@ -37,38 +35,46 @@ val enableSeekbarTappingPatch = bytecodePatch( // Find the required methods to tap the seekbar. val seekbarTappingMethods = onTouchEventHandlerFingerprint.let { - fun getMethodReference(index: Int) = it.method.getInstruction(index) + fun getReference(index: Int) = it.method.getInstruction(index) .reference as MethodReference listOf( - getMethodReference(it.patternMatch!!.startIndex), - getMethodReference(it.patternMatch!!.endIndex) + getReference(it.instructionMatches.first().index), + getReference(it.instructionMatches.last().index) ) } - seekbarTappingFingerprint.method.apply { - val pointIndex = indexOfNewPointInstruction(this) - val invokeIndex = indexOfFirstInstructionOrThrow(pointIndex, Opcode.INVOKE_VIRTUAL) - val insertIndex = invokeIndex + 1 + seekbarTappingFingerprint.let { + val insertIndex = it.instructionMatches.last().index + 1 - val thisInstanceRegister = getInstruction(invokeIndex).registerC - val xAxisRegister = this.getInstruction(pointIndex).registerD - val freeRegister = findFreeRegister(insertIndex, thisInstanceRegister, xAxisRegister) + it.method.apply { + val thisInstanceRegister = getInstruction( + insertIndex - 1 + ).registerC - val oMethod = seekbarTappingMethods[0] - val nMethod = seekbarTappingMethods[1] + val xAxisRegister = this.getInstruction( + it.instructionMatches[2].index + ).registerD - addInstructionsWithLabels( - insertIndex, - """ - invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->seekbarTappingEnabled()Z - move-result v$freeRegister - if-eqz v$freeRegister, :disabled - invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $oMethod - invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $nMethod - """, - ExternalLabel("disabled", getInstruction(insertIndex)), - ) + val freeRegister = findFreeRegister( + insertIndex, thisInstanceRegister, xAxisRegister + ) + + val oMethod = seekbarTappingMethods[0] + val nMethod = seekbarTappingMethods[1] + + addInstructionsWithLabels( + insertIndex, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->seekbarTappingEnabled()Z + move-result v$freeRegister + if-eqz v$freeRegister, :disabled + invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $oMethod + invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $nMethod + """, + ExternalLabel("disabled", getInstruction(insertIndex)), + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt index 8c5dce5551..5ee27ab785 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt @@ -42,7 +42,7 @@ val enableSlideToSeekPatch = bytecodePatch( // Restore the behaviour to slide to seek. - val checkIndex = slideToSeekFingerprint.patternMatch!!.startIndex + val checkIndex = slideToSeekFingerprint.instructionMatches.first().index val checkReference = slideToSeekFingerprint.method.getInstruction(checkIndex) .getReference()!! @@ -75,7 +75,7 @@ val enableSlideToSeekPatch = bytecodePatch( if (is_19_17_or_greater) { disableFastForwardGestureFingerprint.let { it.method.apply { - val targetIndex = it.patternMatch!!.endIndex + val targetIndex = it.instructionMatches.last().index val targetRegister = getInstruction(targetIndex).registerA addInstructions( @@ -88,17 +88,19 @@ val enableSlideToSeekPatch = bytecodePatch( } } } else { - disableFastForwardLegacyFingerprint.method.apply { - val insertIndex = disableFastForwardLegacyFingerprint.patternMatch!!.endIndex + 1 - val targetRegister = getInstruction(insertIndex).registerA - - addInstructions( - insertIndex, - """ - invoke-static { v$targetRegister }, $extensionMethodDescriptor - move-result v$targetRegister - """, - ) + disableFastForwardLegacyFingerprint.let { + it.method.apply { + val insertIndex = it.instructionMatches.last().index + 1 + val targetRegister = getInstruction(insertIndex).registerA + + addInstructions( + insertIndex, + """ + invoke-static { v$targetRegister }, $extensionMethodDescriptor + move-result v$targetRegister + """, + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt index 0b7bf052af..39479e114a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt @@ -1,19 +1,31 @@ package app.revanced.patches.youtube.interaction.seekbar +import app.revanced.patcher.InstructionLocation.MatchAfterImmediately +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.newInstance +import app.revanced.patcher.opcode +import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater +import app.revanced.patches.youtube.misc.playservice.is_19_47_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_19_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_20_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_31_or_greater import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionReversed +import app.revanced.util.indexOfFirstInstruction import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.reference.StringReference internal val swipingUpGestureParentFingerprint = fingerprint { returns("Z") parameters() - literal { 45379021 } + instructions( + literal(45379021) // Swipe up fullscreen feature flag + ) } /** @@ -23,7 +35,9 @@ internal val showSwipingUpGuideFingerprint = fingerprint { accessFlags(AccessFlags.FINAL) returns("Z") parameters() - literal { 1 } + instructions( + literal(1) + ) } /** @@ -39,7 +53,8 @@ internal val disableFastForwardLegacyFingerprint = fingerprint { returns("Z") parameters() opcodes(Opcode.MOVE_RESULT) - literal { 45411330 } + // Intent start flag only used in the subscription activity + literal {45411330} } internal val disableFastForwardGestureFingerprint = fingerprint { @@ -57,6 +72,26 @@ internal val disableFastForwardGestureFingerprint = fingerprint { } } +internal val customTapAndHoldFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + instructions( + literal(2.0f) + ) + custom { method, _ -> + // Code is found in different methods with different strings. + val findSearchLandingKey = (is_19_34_or_greater && !is_19_47_or_greater) + || (is_20_19_or_greater && !is_20_20_or_greater) || is_20_31_or_greater + + method.name == "run" && method.indexOfFirstInstruction { + val string = getReference()?.string + string == "Failed to easy seek haptics vibrate." + || (findSearchLandingKey && string == "search_landing_cache_key") + } >= 0 + } +} + internal val onTouchEventHandlerFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.PUBLIC) returns("Z") @@ -83,18 +118,19 @@ internal val onTouchEventHandlerFingerprint = fingerprint { internal val seekbarTappingFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") - parameters("L") - custom { method, _ -> - method.name == "onTouchEvent" - && method.containsLiteralInstruction(Integer.MAX_VALUE.toLong()) - && indexOfNewPointInstruction(method) >= 0 - } -} + parameters("Landroid/view/MotionEvent;") + instructions( + literal(Int.MAX_VALUE), + + newInstance("Landroid/graphics/Point;"), + methodCall(smali = "Landroid/graphics/Point;->(II)V", location = MatchAfterImmediately()), + methodCall(smali = "Lj\$/util/Optional;->of(Ljava/lang/Object;)Lj\$/util/Optional;", location = MatchAfterImmediately()), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterImmediately()), + fieldAccess(opcode = Opcode.IPUT_OBJECT, type = "Lj\$/util/Optional;", location = MatchAfterImmediately()), -internal fun indexOfNewPointInstruction(method: Method) = method.indexOfFirstInstructionReversed { - val reference = getReference() - reference?.definingClass == "Landroid/graphics/Point;" - && reference.name == "" + opcode(Opcode.INVOKE_VIRTUAL, location = MatchAfterWithin(10)) + ) + custom { method, _ -> method.name == "onTouchEvent" } } internal val slideToSeekFingerprint = fingerprint { @@ -114,5 +150,16 @@ internal val fullscreenSeekbarThumbnailsQualityFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") parameters() - literal { 45399684L } + instructions( + literal(45399684L) // Video stream seekbar thumbnails feature flag. + ) +} + +internal val fullscreenLargeSeekbarFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + instructions( + literal(45691569) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatch.kt index ddf21dd853..d057f91704 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatch.kt @@ -7,10 +7,15 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_20_28_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.seekbarFingerprint import app.revanced.patches.youtube.shared.seekbarOnDrawFingerprint +import app.revanced.util.insertLiteralOverride + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;" val hideSeekbarPatch = bytecodePatch( description = "Adds an option to hide the seekbar.", @@ -20,6 +25,7 @@ val hideSeekbarPatch = bytecodePatch( settingsPatch, seekbarColorPatch, addResourcesPatch, + versionCheckPatch ) execute { @@ -28,19 +34,29 @@ val hideSeekbarPatch = bytecodePatch( PreferenceScreen.SEEKBAR.addPreferences( SwitchPreference("revanced_hide_seekbar"), SwitchPreference("revanced_hide_seekbar_thumbnail"), + SwitchPreference("revanced_fullscreen_large_seekbar"), ) seekbarOnDrawFingerprint.match(seekbarFingerprint.originalClassDef).method.addInstructionsWithLabels( 0, """ const/4 v0, 0x0 - invoke-static { }, Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;->hideSeekbar()Z + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->hideSeekbar()Z move-result v0 if-eqz v0, :hide_seekbar return-void :hide_seekbar nop - """, + """ ) + + if (is_20_28_or_greater) { + fullscreenLargeSeekbarFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->useFullscreenLargeSeekbar(Z)Z" + ) + } + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarPatch.kt index d253488f3f..097adac582 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarPatch.kt @@ -20,10 +20,10 @@ val seekbarPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatch.kt index b502be1ae5..66a79003f8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatch.kt @@ -13,6 +13,7 @@ import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import java.util.logging.Logger private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SeekbarThumbnailsPatch;" diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt index e1161ea13d..9c6eae324d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.interaction.swipecontrols import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal import com.android.tools.smali.dexlib2.AccessFlags internal val swipeControlsHostActivityFingerprint = fingerprint { @@ -12,12 +12,9 @@ internal val swipeControlsHostActivityFingerprint = fingerprint { } } -internal const val SWIPE_CHANGE_VIDEO_FEATURE_FLAG = 45631116L - internal val swipeChangeVideoFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("L") - literal { - SWIPE_CHANGE_VIDEO_FEATURE_FLAG - } + instructions( + literal(45631116L) // Swipe to change fullscreen video feature flag. + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt index a1a9e40a94..77386587f3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt @@ -12,6 +12,9 @@ import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_22_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_34_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.mainActivityConstructorFingerprint @@ -25,12 +28,16 @@ private val swipeControlsResourcePatch = resourcePatch { dependsOn( settingsPatch, addResourcesPatch, + versionCheckPatch, ) execute { addResources("youtube", "interaction.swipecontrols.swipeControlsResourcePatch") - if (is_19_43_or_greater) { + // If fullscreen swipe is enabled in newer versions the app can crash. + // It likely is caused by conflicting experimental flags that are never enabled together. + // Flag was completely removed in 20.34+ + if (is_19_43_or_greater && !is_20_22_or_greater) { PreferenceScreen.SWIPE_CONTROLS.addPreferences( SwitchPreference("revanced_swipe_change_video") ) @@ -88,10 +95,10 @@ val swipeControlsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -122,11 +129,13 @@ val swipeControlsPatch = bytecodePatch( // region patch to enable/disable swipe to change video. - if (is_19_43_or_greater) { - swipeChangeVideoFingerprint.method.insertLiteralOverride( - SWIPE_CHANGE_VIDEO_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z" - ) + if (is_19_43_or_greater && !is_20_34_or_greater) { + swipeChangeVideoFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.last().index, + "$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z" + ) + } } // endregion diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt index b3fbc6b0aa..388d35eb5a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt @@ -24,10 +24,10 @@ val autoCaptionsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -51,7 +51,7 @@ val autoCaptionsPatch = bytecodePatch( """ ) - mapOf( + arrayOf( startVideoInformerFingerprint to 0, storyboardRendererDecoderRecommendedLevelFingerprint to 1 ).forEach { (fingerprint, enabled) -> diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt index 09ca7481d3..bc1a9c781d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt @@ -25,10 +25,10 @@ val customBrandingPatch = baseCustomBrandingPatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt index a7b33cd482..cdb0c7c2d6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt @@ -10,9 +10,9 @@ import app.revanced.patcher.util.Document import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.layout.branding.addBrandLicensePatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.util.ResourceGroup @@ -67,7 +67,7 @@ private val changeHeaderBytecodePatch = bytecodePatch { "yt_ringo2_premium_wordmark_header" ).forEach { resource -> variants.forEach { theme -> - resourceMappings["drawable", resource + "_" + theme] + getResourceId(ResourceType.DRAWABLE, resource + "_" + theme) } } @@ -75,7 +75,7 @@ private val changeHeaderBytecodePatch = bytecodePatch { "ytWordmarkHeader", "ytPremiumWordmarkHeader" ).forEach { resourceName -> - val resourceId = resourceMappings["attr", resourceName] + val resourceId = getResourceId(ResourceType.ATTR, resourceName) forEachLiteralValueInstruction(resourceId) { literalIndex -> val register = getInstruction(literalIndex).registerA @@ -100,10 +100,10 @@ val changeHeaderPatch = resourcePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt index 49fa65dfed..15cadfd63a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt @@ -8,7 +8,10 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.playservice.is_20_22_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import java.util.logging.Logger val hideButtonsPatch = resourcePatch( name = "Hide video action buttons", @@ -18,41 +21,60 @@ val hideButtonsPatch = resourcePatch( resourceMappingPatch, lithoFilterPatch, addResourcesPatch, + versionCheckPatch, ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + // 20.22+ does not yet support hiding all player buttons. ) ) execute { addResources("youtube", "layout.buttons.action.hideButtonsPatch") - PreferenceScreen.PLAYER.addPreferences( - PreferenceScreenPreference( - "revanced_hide_buttons_screen", - preferences = setOf( - SwitchPreference("revanced_disable_like_subscribe_glow"), + val preferences = mutableSetOf( + SwitchPreference("revanced_disable_like_subscribe_glow"), + SwitchPreference("revanced_hide_download_button"), + SwitchPreference("revanced_hide_like_dislike_button"), + SwitchPreference("revanced_hide_comments_button"), + SwitchPreference("revanced_hide_save_button"), + ) + + if (is_20_22_or_greater) { + // FIXME: 20.22+ filtering of the action buttons doesn't work because + // the buffer is the same for all buttons. + Logger.getLogger(this::class.java.name).warning( + "\n!!!" + + "\n!!! Not all player action buttons can be set hidden when patching 20.22+" + + "\n!!! Patch 20.21.37 or lower if you want to hide player action buttons" + + "\n!!!" + ) + } else { + preferences.addAll( + listOf( + SwitchPreference("revanced_hide_hype_button"), SwitchPreference("revanced_hide_ask_button"), SwitchPreference("revanced_hide_clip_button"), - SwitchPreference("revanced_hide_comments_button"), - SwitchPreference("revanced_hide_download_button"), - SwitchPreference("revanced_hide_hype_button"), - SwitchPreference("revanced_hide_like_dislike_button"), SwitchPreference("revanced_hide_promote_button"), SwitchPreference("revanced_hide_remix_button"), SwitchPreference("revanced_hide_report_button"), - SwitchPreference("revanced_hide_save_button"), SwitchPreference("revanced_hide_share_button"), SwitchPreference("revanced_hide_shop_button"), SwitchPreference("revanced_hide_stop_ads_button"), SwitchPreference("revanced_hide_thanks_button"), ) ) + } + + PreferenceScreen.PLAYER.addPreferences( + PreferenceScreenPreference( + "revanced_hide_buttons_screen", + preferences = preferences + ) ) addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;") diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt index a4e65eb28b..e7168be19a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt @@ -1,53 +1,69 @@ package app.revanced.patches.youtube.layout.buttons.navigation -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.InstructionLocation.MatchAfterImmediately import app.revanced.patcher.fingerprint -import app.revanced.util.literal - -internal const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive" +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode internal val addCreateButtonViewFingerprint = fingerprint { - strings("Android Wear", ANDROID_AUTOMOTIVE_STRING) + instructions( + string("Android Wear"), + opcode(Opcode.IF_EQZ), + string("Android Automotive", location = MatchAfterImmediately()), + ) } internal val createPivotBarFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") parameters( "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;", "Landroid/widget/TextView;", "Ljava/lang/CharSequence;", ) - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.RETURN_VOID, + instructions( + methodCall(definingClass = "Landroid/widget/TextView;", name = "setText"), + opcode(Opcode.RETURN_VOID) ) } -internal const val TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG = 45400535L +internal val animatedNavigationTabsFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + instructions( + literal(45680008L) + ) +} internal val translucentNavigationStatusBarFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") - literal { TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG } + instructions( + literal(45400535L) // Translucent status bar feature flag. + ) } -internal const val TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG = 45630927L - +/** + * YouTube nav buttons. + */ internal val translucentNavigationButtonsFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") - literal { TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG } + instructions( + literal(45630927L) // Translucent navigation bar buttons feature flag. + ) } /** - * The device on screen back/home/recent buttons. + * Device on screen back/home/recent buttons. */ -internal const val TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG = 45632194L - internal val translucentNavigationButtonsSystemFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") - literal { TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG } -} \ No newline at end of file + instructions( + literal(45632194L) // Translucent system buttons feature flag. + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt index 6175aa4bd8..1e88e1b854 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt @@ -13,15 +13,13 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_15_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.insertLiteralOverride import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;" @@ -40,10 +38,10 @@ val navigationButtonsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -69,6 +67,12 @@ val navigationButtonsPatch = bytecodePatch( ) } + if (is_20_15_or_greater) { + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + SwitchPreference("revanced_navigation_bar_animations") + ) + } + PreferenceScreen.GENERAL_LAYOUT.addPreferences( PreferenceScreenPreference( key = "revanced_navigation_buttons_screen", @@ -78,59 +82,70 @@ val navigationButtonsPatch = bytecodePatch( ) // Switch create with notifications button. - addCreateButtonViewFingerprint.method.apply { - val stringIndex = addCreateButtonViewFingerprint.stringMatches!!.find { match -> - match.string == ANDROID_AUTOMOTIVE_STRING - }!!.index - - val conditionalCheckIndex = stringIndex - 1 - val conditionRegister = - getInstruction(conditionalCheckIndex).registerA - - addInstructions( - conditionalCheckIndex, - """ - invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z - move-result v$conditionRegister - """, - ) + addCreateButtonViewFingerprint.let { + it.method.apply { + val conditionalCheckIndex = it.instructionMatches[1].index + val conditionRegister = + getInstruction(conditionalCheckIndex).registerA + + addInstructions( + conditionalCheckIndex, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z + move-result v$conditionRegister + """, + ) + } } // Hide navigation button labels. - createPivotBarFingerprint.method.apply { - val setTextIndex = indexOfFirstInstructionOrThrow { - getReference()?.name == "setText" + createPivotBarFingerprint.let { + it.method.apply { + val setTextIndex = it.instructionMatches.first().index + val targetRegister = getInstruction(setTextIndex).registerC + + addInstruction( + setTextIndex, + "invoke-static { v$targetRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->hideNavigationButtonLabels(Landroid/widget/TextView;)V", + ) } - - val targetRegister = getInstruction(setTextIndex).registerC - - addInstruction( - setTextIndex, - "invoke-static { v$targetRegister }, " + - "$EXTENSION_CLASS_DESCRIPTOR->hideNavigationButtonLabels(Landroid/widget/TextView;)V", - ) } // Hook navigation button created, in order to hide them. hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR) - // Force on/off translucent effect on status bar and navigation buttons. if (is_19_25_or_greater) { - translucentNavigationStatusBarFeatureFlagFingerprint.method.insertLiteralOverride( - TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z", - ) + translucentNavigationStatusBarFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z", + ) + } - translucentNavigationButtonsFeatureFlagFingerprint.method.insertLiteralOverride( - TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", - ) + translucentNavigationButtonsFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", + ) + } - translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertLiteralOverride( - TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", - ) + translucentNavigationButtonsSystemFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", + ) + } + } + + if (is_20_15_or_greater) { + animatedNavigationTabsFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->useAnimatedNavigationButtons(Z)Z" + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt index 7cc63f0fe6..bec02d7566 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt @@ -1,20 +1,12 @@ package app.revanced.patches.youtube.layout.buttons.overlay import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags -internal val playerControlsPreviousNextOverlayTouchFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - strings("1.0x") - custom { methodDef, _ -> - methodDef.containsLiteralInstruction(playerControlPreviousButtonTouchArea) && - methodDef.containsLiteralInstruction(playerControlNextButtonTouchArea) - } -} - internal val mediaRouteButtonFingerprint = fingerprint { parameters("I") custom { methodDef, _ -> @@ -22,9 +14,26 @@ internal val mediaRouteButtonFingerprint = fingerprint { } } +internal val castButtonPlayerFeatureFlagFingerprint = fingerprint { + returns("Z") + instructions( + literal(45690091) + ) +} + +internal val castButtonActionFeatureFlagFingerprint = fingerprint { + returns("Z") + instructions( + literal(45690090) + ) +} + internal val inflateControlsGroupLayoutStubFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters() returns("V") - literal { controlsButtonGroupLayoutStub } + instructions( + resourceLiteral(ResourceType.ID, "youtube_controls_button_group_layout_stub"), + methodCall(name = "inflate") + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt index 86242d1048..42e8a2176e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt @@ -5,42 +5,28 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_20_28_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.layoutConstructorFingerprint import app.revanced.patches.youtube.shared.subtitleButtonControllerFingerprint -import app.revanced.util.* +import app.revanced.util.findFreeRegister +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstResourceIdOrThrow +import app.revanced.util.insertLiteralOverride import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -internal var playerControlPreviousButtonTouchArea = -1L - private set -internal var playerControlNextButtonTouchArea = -1L - private set -internal var controlsButtonGroupLayoutStub = -1L - private set - -private val hidePlayerOverlayButtonsResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - - execute { - playerControlPreviousButtonTouchArea = resourceMappings["id", "player_control_previous_button_touch_area"] - playerControlNextButtonTouchArea = resourceMappings["id", "player_control_next_button_touch_area"] - controlsButtonGroupLayoutStub = resourceMappings["id", "youtube_controls_button_group_layout_stub"] - } -} - private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HidePlayerOverlayButtonsPatch;" @@ -53,15 +39,16 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( sharedExtensionPatch, settingsPatch, addResourcesPatch, - hidePlayerOverlayButtonsResourcePatch, + resourceMappingPatch, // Used by fingerprints. + versionCheckPatch ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -78,21 +65,19 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( // region Hide player next/previous button. - playerControlsPreviousNextOverlayTouchFingerprint.method.apply { - val resourceIndex = indexOfFirstLiteralInstructionOrThrow(playerControlPreviousButtonTouchArea) + layoutConstructorFingerprint.let { + it.clearMatch() // Fingerprint is shared with other patches. - val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) { - opcode == Opcode.INVOKE_STATIC && - getReference()?.parameterTypes?.firstOrNull() == "Landroid/view/View;" - } - - val viewRegister = getInstruction(insertIndex).registerC + it.method.apply { + val insertIndex = it.instructionMatches.last().index + val viewRegister = getInstruction(insertIndex).registerC - addInstruction( - insertIndex, - "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR" + - "->hidePreviousNextButtons(Landroid/view/View;)V", - ) + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR" + + "->hidePreviousNextButtons(Landroid/view/View;)V", + ) + } } // endregion @@ -104,20 +89,33 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( """ invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getCastButtonOverrideV2(I)I move-result p1 - """, + """ ) + if (is_20_28_or_greater) { + arrayOf( + castButtonPlayerFeatureFlagFingerprint, + castButtonActionFeatureFlagFingerprint + ).forEach { fingerprint -> + fingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->getCastButtonOverrideV2(Z)Z" + ) + } + } + } + // endregion // region Hide captions button. subtitleButtonControllerFingerprint.method.apply { - // Due to previously applied patches, scanResult index cannot be used in this context val insertIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1 addInstruction( insertIndex, - "invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->hideCaptionsButton(Landroid/widget/ImageView;)V", + "invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->hideCaptionsButton(Landroid/widget/ImageView;)V", ) } @@ -152,27 +150,21 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( // region Hide player control buttons background. - inflateControlsGroupLayoutStubFingerprint.method.apply { - val controlsButtonGroupLayoutStubResIdConstIndex = - indexOfFirstLiteralInstructionOrThrow(controlsButtonGroupLayoutStub) - val inflateControlsGroupLayoutStubIndex = - indexOfFirstInstruction(controlsButtonGroupLayoutStubResIdConstIndex) { - getReference()?.name == "inflate" - } - - val freeRegister = findFreeRegister(inflateControlsGroupLayoutStubIndex) - val hidePlayerControlButtonsBackgroundDescriptor = - "$EXTENSION_CLASS_DESCRIPTOR->hidePlayerControlButtonsBackground(Landroid/view/View;)V" - - addInstructions( - inflateControlsGroupLayoutStubIndex + 1, - """ - # Move the inflated layout to a temporary register. - # The result of the inflate method is by default not moved to a register after the method is called. - move-result-object v$freeRegister - invoke-static { v$freeRegister }, $hidePlayerControlButtonsBackgroundDescriptor - """ - ) + inflateControlsGroupLayoutStubFingerprint.let { + it.method.apply { + val insertIndex = it.instructionMatches.last().index + 1 + val freeRegister = findFreeRegister(insertIndex) + + addInstructions( + insertIndex, + """ + # Move the inflated layout to a temporary register. + # The result of the inflate method is by default not moved to a register after the method is called. + move-result-object v$freeRegister + invoke-static { v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->hidePlayerControlButtonsBackground(Landroid/view/View;)V + """ + ) + } } // endregion diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatch.kt index ba599b1d68..a7eb6e3947 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatch.kt @@ -1,7 +1,10 @@ package app.revanced.patches.youtube.layout.formfactor +import app.revanced.patcher.InstructionLocation.* import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.fieldAccess +import app.revanced.patcher.fingerprint import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -11,11 +14,8 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.FieldReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;" @@ -33,10 +33,10 @@ val changeFormFactorPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -49,24 +49,33 @@ val changeFormFactorPatch = bytecodePatch( hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR) - createPlayerRequestBodyWithModelFingerprint.method.apply { - val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type + val createPlayerRequestBodyWithModelFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters() + instructions( + fieldAccess(smali = "Landroid/os/Build;->MODEL:Ljava/lang/String;"), + fieldAccess( + definingClass = formFactorEnumConstructorFingerprint.originalClassDef.type, + type = "I", + location = MatchAfterWithin(50) + ) + ) + } - val index = indexOfFirstInstructionOrThrow { - val reference = getReference() - opcode == Opcode.IGET && - reference?.definingClass == formFactorEnumClass && - reference.type == "I" - } - val register = getInstruction(index).registerA + createPlayerRequestBodyWithModelFingerprint.let { + it.method.apply { + val index = it.instructionMatches.last().index + val register = getInstruction(index).registerA - addInstructions( - index + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getFormFactor(I)I - move-result v$register - """ - ) + addInstructions( + index + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getFormFactor(I)I + move-result v$register + """ + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/Fingerprints.kt index d1f1535ebf..457d19116e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/formfactor/Fingerprints.kt @@ -1,12 +1,7 @@ package app.revanced.patches.youtube.layout.formfactor import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.reference.FieldReference internal val formFactorEnumConstructorFingerprint = fingerprint { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) @@ -17,33 +12,3 @@ internal val formFactorEnumConstructorFingerprint = fingerprint { "AUTOMOTIVE_FORM_FACTOR" ) } - -internal val createPlayerRequestBodyWithModelFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() - opcodes(Opcode.OR_INT_LIT16) - custom { method, _ -> - method.indexOfModelInstruction() >= 0 && - method.indexOfReleaseInstruction() >= 0 - } -} - -private fun Method.indexOfModelInstruction() = - indexOfFirstInstruction { - val reference = getReference() - - reference?.definingClass == "Landroid/os/Build;" && - reference.name == "MODEL" && - reference.type == "Ljava/lang/String;" - } - -internal fun Method.indexOfReleaseInstruction(): Int = - indexOfFirstInstruction { - val reference = getReference() - - reference?.definingClass == "Landroid/os/Build${'$'}VERSION;" && - reference.name == "RELEASE" && - reference.type == "Ljava/lang/String;" - } - diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt index d39a639fa1..79fa1b269a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt @@ -10,6 +10,8 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.reference.FieldReference internal val layoutCircleFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() returns("Landroid/view/View;") opcodes( Opcode.CONST, @@ -22,6 +24,8 @@ internal val layoutCircleFingerprint = fingerprint { } internal val layoutIconFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() returns("Landroid/view/View;") opcodes( Opcode.INVOKE_VIRTUAL, @@ -33,6 +37,8 @@ internal val layoutIconFingerprint = fingerprint { } internal val layoutVideoFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + parameters() returns("Landroid/view/View;") opcodes( Opcode.CONST, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndScreenCardsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndScreenCardsPatch.kt index d09de1d2fc..8aa095ec12 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndScreenCardsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndScreenCardsPatch.kt @@ -7,9 +7,9 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater @@ -39,7 +39,7 @@ private val hideEndScreenCardsResourcePatch = resourcePatch { SwitchPreference("revanced_hide_endscreen_cards"), ) - fun idOf(name: String) = resourceMappings["layout", "endscreen_element_layout_$name"] + fun idOf(name: String) = getResourceId(ResourceType.LAYOUT, "endscreen_element_layout_$name") layoutCircle = idOf("circle") layoutIcon = idOf("icon") @@ -63,10 +63,10 @@ val hideEndScreenCardsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -77,7 +77,7 @@ val hideEndScreenCardsPatch = bytecodePatch( layoutVideoFingerprint, ).forEach { fingerprint -> fingerprint.method.apply { - val insertIndex = fingerprint.patternMatch!!.endIndex + 1 + val insertIndex = fingerprint.instructionMatches.last().index + 1 val viewRegister = getInstruction(insertIndex - 1).registerA addInstruction( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatch.kt index 67658339e9..043791a988 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatch.kt @@ -31,10 +31,10 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -46,7 +46,7 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch( ) removeOnLayoutChangeListenerFingerprint.let { - val endScreenMethod = navigate(it.originalMethod).to(it.patternMatch!!.endIndex).stop() + val endScreenMethod = navigate(it.originalMethod).to(it.instructionMatches.last().index).stop() endScreenMethod.apply { val autoNavStatusMethodName = autoNavStatusFingerprint.match( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt index 7edea5fba5..9133bdc073 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt @@ -29,10 +29,10 @@ val disableFullscreenAmbientModePatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt index 22be7c3de8..7e80b2bd97 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt @@ -1,65 +1,60 @@ package app.revanced.patches.youtube.layout.hide.general +import app.revanced.patcher.InstructionLocation.* +import app.revanced.patcher.StringComparisonType +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutFingerprint import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val hideShowMoreButtonFingerprint = fingerprint { - opcodes( - Opcode.CONST, - Opcode.CONST_4, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - ) - literal { expandButtonDownId } -} - /** - * 20.12+ + * 20.26+ */ -internal val parseElementFromBufferFingerprint = fingerprint { - parameters("L", "L", "[B", "L", "L") - opcodes( - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, +internal val hideShowMoreButtonFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL, AccessFlags.SYNTHETIC) + returns("V") + parameters("L", "Ljava/lang/Object;") + instructions( + resourceLiteral(ResourceType.LAYOUT, "expand_button_down"), + methodCall(smali = "Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View;"), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterImmediately()) ) - strings("Failed to parse Element") // String is a partial match. } -/** - * 20.07+ - */ -internal val parseElementFromBufferLegacy2007Fingerprint = fingerprint { - parameters("L", "L", "[B", "L", "L") - opcodes( - Opcode.IGET_OBJECT, - Opcode.IGET_BOOLEAN, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, +internal val hideShowMoreLegacyButtonFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + instructions( + resourceLiteral(ResourceType.LAYOUT, "expand_button_down"), + methodCall(smali = "Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;"), + opcode(Opcode.MOVE_RESULT_OBJECT) ) - strings("Failed to parse Element") // String is a partial match. } -/** - * 19.01 - 20.06 - */ -internal val parseElementFromBufferLegacy1901Fingerprint = fingerprint { +internal val parseElementFromBufferFingerprint = fingerprint { parameters("L", "L", "[B", "L", "L") - opcodes( - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, + instructions( + opcode(Opcode.IGET_OBJECT), + // IGET_BOOLEAN // 20.07+ + opcode(Opcode.INVOKE_INTERFACE, location = MatchAfterWithin(1)), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterImmediately()), + + string("Failed to parse Element", StringComparisonType.STARTS_WITH) ) - strings("Failed to parse Element") // String is a partial match. } internal val playerOverlayFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") - strings("player_overlay_in_video_programming") + instructions( + string("player_overlay_in_video_programming") + ) } internal val showWatermarkFingerprint = fingerprint { @@ -75,7 +70,9 @@ internal val yoodlesImageViewFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Landroid/view/View;") parameters("L", "L") - literal { youTubeLogo } + instructions( + resourceLiteral(ResourceType.ID, "youtube_logo") + ) } internal val crowdfundingBoxFingerprint = fingerprint { @@ -103,7 +100,6 @@ internal val albumCardsFingerprint = fingerprint { internal val filterBarHeightFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CONST, Opcode.INVOKE_VIRTUAL, @@ -115,7 +111,6 @@ internal val filterBarHeightFingerprint = fingerprint { internal val relatedChipCloudFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CONST, Opcode.INVOKE_VIRTUAL, @@ -126,7 +121,6 @@ internal val relatedChipCloudFingerprint = fingerprint { internal val searchResultsChipBarFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CONST, Opcode.INVOKE_VIRTUAL, @@ -141,11 +135,11 @@ internal val showFloatingMicrophoneButtonFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters() - opcodes( - Opcode.IGET_BOOLEAN, - Opcode.IF_EQZ, + instructions( + resourceLiteral(ResourceType.ID, "fab"), + checkCast("/FloatingActionButton;", location = MatchAfterWithin(10)), + opcode(Opcode.IGET_BOOLEAN, location = MatchAfterWithin(15)) ) - literal { fabButtonId } } internal val hideViewCountFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 36959f2299..a6220f6c22 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -14,22 +14,24 @@ import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings -import app.revanced.patches.shared.misc.settings.preference.* +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch -import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater -import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_26_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.util.findFreeRegister import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction @@ -38,66 +40,45 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference import app.revanced.util.indexOfFirstInstructionReversedOrThrow -var expandButtonDownId = -1L +internal var albumCardId = -1L private set -var albumCardId = -1L +internal var crowdfundingBoxId = -1L private set -var crowdfundingBoxId = -1L +internal var filterBarHeightId = -1L private set -var youTubeLogo = -1L +internal var relatedChipCloudMarginId = -1L private set -var filterBarHeightId = -1L - private set -var relatedChipCloudMarginId = -1L - private set -var barContainerHeightId = -1L - private set -var fabButtonId = -1L +internal var barContainerHeightId = -1L private set private val hideLayoutComponentsResourcePatch = resourcePatch { dependsOn(resourceMappingPatch) execute { - expandButtonDownId = resourceMappings[ - "layout", - "expand_button_down", - ] - - albumCardId = resourceMappings[ - "layout", + albumCardId = getResourceId( + ResourceType.LAYOUT, "album_card", - ] + ) - crowdfundingBoxId = resourceMappings[ - "layout", + crowdfundingBoxId = getResourceId( + ResourceType.LAYOUT, "donation_companion", - ] - - youTubeLogo = resourceMappings[ - "id", - "youtube_logo", - ] + ) - relatedChipCloudMarginId = resourceMappings[ - "layout", + relatedChipCloudMarginId = getResourceId( + ResourceType.LAYOUT, "related_chip_cloud_reduced_margins", - ] + ) - filterBarHeightId = resourceMappings[ - "dimen", + filterBarHeightId = getResourceId( + ResourceType.DIMEN, "filter_bar_height", - ] + ) - barContainerHeightId = resourceMappings[ - "dimen", + barContainerHeightId = getResourceId( + ResourceType.DIMEN, "bar_container_height", - ] - - fabButtonId = resourceMappings[ - "id", - "fab", - ] + ) } } @@ -123,14 +104,16 @@ val hideLayoutComponentsPatch = bytecodePatch( addResourcesPatch, hideLayoutComponentsResourcePatch, navigationBarHookPatch, + versionCheckPatch, + resourceMappingPatch ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -267,32 +250,29 @@ val hideLayoutComponentsPatch = bytecodePatch( // region Mix playlists - (if (is_20_09_or_greater) parseElementFromBufferFingerprint - else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint - else parseElementFromBufferLegacy1901Fingerprint).let { - it.method.apply { - val byteArrayParameter = "p3" - val startIndex = it.patternMatch!!.startIndex - val conversionContextRegister = getInstruction(startIndex).registerA - val returnEmptyComponentInstruction = instructions.last { it.opcode == Opcode.INVOKE_STATIC } - val returnEmptyComponentRegister = (returnEmptyComponentInstruction as FiveRegisterInstruction).registerC - val insertIndex = startIndex + 1 - val freeRegister = findFreeRegister(insertIndex, conversionContextRegister, returnEmptyComponentRegister) - - addInstructionsWithLabels( - insertIndex, - """ - invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z - move-result v$freeRegister - if-eqz v$freeRegister, :show - move-object v$returnEmptyComponentRegister, p1 # Required for 19.47 - goto :return_empty_component - :show - nop - """, - ExternalLabel("return_empty_component", returnEmptyComponentInstruction), - ) - } + parseElementFromBufferFingerprint.method.apply { + val startIndex = parseElementFromBufferFingerprint.instructionMatches.first().index + val insertIndex = startIndex + 1 + + val byteArrayParameter = "p3" + val conversionContextRegister = getInstruction(startIndex).registerA + val returnEmptyComponentInstruction = instructions.last { it.opcode == Opcode.INVOKE_STATIC } + val returnEmptyComponentRegister = (returnEmptyComponentInstruction as FiveRegisterInstruction).registerC + val freeRegister = findFreeRegister(insertIndex, conversionContextRegister, returnEmptyComponentRegister) + + addInstructionsWithLabels( + insertIndex, + """ + invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z + move-result v$freeRegister + if-eqz v$freeRegister, :show + move-object v$returnEmptyComponentRegister, p1 # Required for 19.47 + goto :return_empty_component + :show + nop + """, + ExternalLabel("return_empty_component", returnEmptyComponentInstruction), + ) } // endregion @@ -318,16 +298,18 @@ val hideLayoutComponentsPatch = bytecodePatch( // region Show more button - hideShowMoreButtonFingerprint.method.apply { - val moveRegisterIndex = hideShowMoreButtonFingerprint.patternMatch!!.endIndex - val viewRegister = getInstruction(moveRegisterIndex).registerA + (if (is_20_26_or_greater) hideShowMoreButtonFingerprint else hideShowMoreLegacyButtonFingerprint).let { + it.method.apply { + val moveRegisterIndex = it.instructionMatches.last().index + val viewRegister = getInstruction(moveRegisterIndex).registerA - val insertIndex = moveRegisterIndex + 1 - addInstruction( - insertIndex, - "invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + - "->hideShowMoreButton(Landroid/view/View;)V", - ) + val insertIndex = moveRegisterIndex + 1 + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + + "->hideShowMoreButton(Landroid/view/View;)V", + ) + } } // endregion @@ -335,7 +317,7 @@ val hideLayoutComponentsPatch = bytecodePatch( // region crowdfunding box crowdfundingBoxFingerprint.let { it.method.apply { - val insertIndex = it.patternMatch!!.endIndex + val insertIndex = it.instructionMatches.last().index val objectRegister = getInstruction(insertIndex).registerA addInstruction( @@ -352,7 +334,7 @@ val hideLayoutComponentsPatch = bytecodePatch( albumCardsFingerprint.let { it.method.apply { - val checkCastAnchorIndex = it.patternMatch!!.endIndex + val checkCastAnchorIndex = it.instructionMatches.last().index val insertIndex = checkCastAnchorIndex + 1 val register = getInstruction(checkCastAnchorIndex).registerA @@ -368,18 +350,19 @@ val hideLayoutComponentsPatch = bytecodePatch( // region hide floating microphone - showFloatingMicrophoneButtonFingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(fabButtonId) - val booleanIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.IGET_BOOLEAN) - val register = getInstruction(booleanIndex).registerA + showFloatingMicrophoneButtonFingerprint.let { + it.method.apply { + val index = it.instructionMatches.last().index + val register = getInstruction(index).registerA - addInstructions( - booleanIndex + 1, - """ - invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z - move-result v$register - """ - ) + addInstructions( + index + 1, + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z + move-result v$register + """, + ) + } } // endregion @@ -451,11 +434,9 @@ val hideLayoutComponentsPatch = bytecodePatch( hookRegisterOffset: Int = 0, instructions: (Int) -> String, ) = method.apply { - val endIndex = patternMatch!!.endIndex - + val endIndex = instructionMatches.last().index val insertIndex = endIndex + insertIndexOffset - val register = - getInstruction(endIndex + hookRegisterOffset).registerA + val register = getInstruction(endIndex + hookRegisterOffset).registerA addInstructions(insertIndex, instructions(register)) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt index 5088472a1b..e9432aa83d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt @@ -1,6 +1,7 @@ package app.revanced.patches.youtube.layout.hide.infocards import app.revanced.patcher.fingerprint +import app.revanced.patcher.string import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -9,13 +10,17 @@ internal val infocardsIncognitoFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Ljava/lang/Boolean;") parameters("L", "J") - strings("vibrator") + instructions( + string("vibrator") + ) } internal val infocardsIncognitoParentFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Ljava/lang/String;") - strings("player_overlay_info_card_teaser") + instructions( + string("player_overlay_info_card_teaser") + ) } internal val infocardsMethodCallFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt index 09c6487389..b2cb6e03d6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt @@ -8,9 +8,9 @@ import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter @@ -25,22 +25,15 @@ internal var drawerResourceId = -1L private set private val hideInfocardsResourcePatch = resourcePatch { - dependsOn( - settingsPatch, - resourceMappingPatch, - addResourcesPatch, - ) - execute { - addResources("youtube", "layout.hide.infocards.hideInfocardsResourcePatch") - - PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_info_cards"), + dependsOn(resourceMappingPatch ) + + execute { - drawerResourceId = resourceMappings[ - "id", + drawerResourceId = getResourceId( + ResourceType.ID, "info_cards_drawer_header", - ] + ) } } @@ -53,18 +46,27 @@ val hideInfoCardsPatch = bytecodePatch( sharedExtensionPatch, lithoFilterPatch, hideInfocardsResourcePatch, + settingsPatch, + addResourcesPatch, ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) execute { + addResources("youtube", "layout.hide.infocards.hideInfocardsResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_hide_info_cards"), + ) + + // Edit: This old non litho code may be obsolete and no longer used by any supported versions. infocardsIncognitoFingerprint.match(infocardsIncognitoParentFingerprint.originalClassDef).method.apply { val invokeInstructionIndex = implementation!!.instructions.indexOfFirst { it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal && @@ -78,23 +80,26 @@ val hideInfoCardsPatch = bytecodePatch( ) } - val hideInfoCardsCallMethod = infocardsMethodCallFingerprint.method - - val invokeInterfaceIndex = infocardsMethodCallFingerprint.patternMatch!!.endIndex - val toggleRegister = infocardsMethodCallFingerprint.method.implementation!!.registerCount - 1 + // Edit: This old non litho code may be obsolete and no longer used by any supported versions. + infocardsMethodCallFingerprint.let { + val invokeInterfaceIndex = it.instructionMatches.last().index + it.method.apply { + val register = implementation!!.registerCount - 1 - hideInfoCardsCallMethod.addInstructionsWithLabels( - invokeInterfaceIndex, - """ - invoke-static {}, Lapp/revanced/extension/youtube/patches/HideInfoCardsPatch;->hideInfoCardsMethodCall()Z - move-result v$toggleRegister - if-nez v$toggleRegister, :hide_info_cards - """, - ExternalLabel( - "hide_info_cards", - hideInfoCardsCallMethod.getInstruction(invokeInterfaceIndex + 1), - ), - ) + addInstructionsWithLabels( + invokeInterfaceIndex, + """ + invoke-static {}, Lapp/revanced/extension/youtube/patches/HideInfoCardsPatch;->hideInfoCardsMethodCall()Z + move-result v$register + if-nez v$register, :hide_info_cards + """, + ExternalLabel( + "hide_info_cards", + getInstruction(invokeInterfaceIndex + 1), + ) + ) + } + } // Info cards can also appear as Litho components. val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/components/HideInfoCardsFilter;" diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt index 4ee4e92594..8c07b34244 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt @@ -24,10 +24,10 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/Fingerprints.kt index 0ca1298631..15ae61df1d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/Fingerprints.kt @@ -1,11 +1,14 @@ package app.revanced.patches.youtube.layout.hide.relatedvideooverlay import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral internal val relatedEndScreenResultsParentFingerprint = fingerprint { returns("V") - literal{ appRelatedEndScreenResults } + instructions( + resourceLiteral(ResourceType.LAYOUT, "app_related_endscreen_results") + ) } internal val relatedEndScreenResultsFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatch.kt index f8f1f3f5fe..aa50c45492 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatch.kt @@ -3,33 +3,14 @@ package app.revanced.patches.youtube.layout.hide.relatedvideooverlay import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.patcher.util.smali.ExternalLabel - -internal var appRelatedEndScreenResults = -1L - private set - -private val hideRelatedVideoOverlayResourcePatch = resourcePatch { - dependsOn( - resourceMappingPatch, - ) - - execute { - appRelatedEndScreenResults = resourceMappings[ - "layout", - "app_related_endscreen_results", - ] - } -} private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideRelatedVideoOverlayPatch;" @@ -43,15 +24,15 @@ val hideRelatedVideoOverlayPatch = bytecodePatch( settingsPatch, sharedExtensionPatch, addResourcesPatch, - hideRelatedVideoOverlayResourcePatch, + resourceMappingPatch, ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt index 2803497727..d9ef584d96 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt @@ -29,10 +29,10 @@ val disableRollingNumberAnimationPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -45,27 +45,27 @@ val disableRollingNumberAnimationPatch = bytecodePatch( // Animations are disabled by preventing an Image from being applied to the text span, // which prevents the animations from appearing. + rollingNumberTextViewAnimationUpdateFingerprint.let { + val blockStartIndex = it.instructionMatches.first().index + val blockEndIndex = it.instructionMatches.last().index + 1 + it.method.apply { + val freeRegister = getInstruction(blockStartIndex).registerA - val patternMatch = rollingNumberTextViewAnimationUpdateFingerprint.patternMatch!! - val blockStartIndex = patternMatch.startIndex - val blockEndIndex = patternMatch.endIndex + 1 - rollingNumberTextViewAnimationUpdateFingerprint.method.apply { - val freeRegister = getInstruction(blockStartIndex).registerA + // ReturnYouTubeDislike also makes changes to this same method, + // and must add control flow label to a noop instruction to + // ensure RYD patch adds its changes after the control flow label. + addInstructions(blockEndIndex, "nop") - // ReturnYouTubeDislike also makes changes to this same method, - // and must add control flow label to a noop instruction to - // ensure RYD patch adds its changes after the control flow label. - addInstructions(blockEndIndex, "nop") - - addInstructionsWithLabels( - blockStartIndex, - """ + addInstructionsWithLabels( + blockStartIndex, + """ invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableRollingNumberAnimations()Z move-result v$freeRegister if-nez v$freeRegister, :disable_animations """, - ExternalLabel("disable_animations", getInstruction(blockEndIndex)), - ) + ExternalLabel("disable_animations", getInstruction(blockEndIndex)), + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt deleted file mode 100644 index edf7390cef..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.seekbar - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.interaction.seekbar.hideSeekbarPatch - -@Deprecated("Patch was moved to app.revanced.patches.youtube.interaction.seekbar") -val hideSeekbarPatch = bytecodePatch { - dependsOn( - hideSeekbarPatch - ) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt index cc883425ba..e33965c625 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt @@ -1,52 +1,71 @@ package app.revanced.patches.youtube.layout.hide.shorts +import app.revanced.patcher.InstructionLocation.* import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val legacyRenderBottomNavigationBarParentFingerprint = fingerprint { - parameters( - "I", - "I", - "L", - "L", - "J", - "L", - ) - strings("aa") -} - internal val shortsBottomBarContainerFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("Landroid/view/View;", "Landroid/os/Bundle;") - strings("r_pfvc") - literal { bottomBarContainer } + instructions( + string("r_pfvc"), + resourceLiteral(ResourceType.ID, "bottom_bar_container"), + methodCall(name = "getHeight"), + opcode(Opcode.MOVE_RESULT) + ) } +/** + * 19.41 to 20.44. + */ internal val renderBottomNavigationBarFingerprint = fingerprint { returns("V") parameters("Ljava/lang/String;") - opcodes( - Opcode.IGET_OBJECT, - Opcode.MONITOR_ENTER, - Opcode.IGET_OBJECT, - Opcode.IF_EQZ, - Opcode.INVOKE_INTERFACE, - Opcode.MONITOR_EXIT, - Opcode.RETURN_VOID, - Opcode.MOVE_EXCEPTION, - Opcode.MONITOR_EXIT, - Opcode.THROW, + instructions( + opcode(Opcode.IGET_OBJECT, MatchFirst()), + opcode(Opcode.MONITOR_ENTER, MatchAfterImmediately()), + opcode(Opcode.IGET_OBJECT, MatchAfterImmediately()), + opcode(Opcode.IF_EQZ, MatchAfterImmediately()), + opcode(Opcode.INVOKE_INTERFACE, MatchAfterImmediately()), + + opcode(Opcode.MONITOR_EXIT), + opcode(Opcode.RETURN_VOID, MatchAfterImmediately()), + opcode(Opcode.MOVE_EXCEPTION, MatchAfterImmediately()), + opcode(Opcode.MONITOR_EXIT, MatchAfterImmediately()), + opcode(Opcode.THROW, MatchAfterImmediately()), ) } /** - * Identical to [legacyRenderBottomNavigationBarParentFingerprint] + * Less than 19.41. + */ +internal val legacyRenderBottomNavigationBarLegacyParentFingerprint = fingerprint { + parameters( + "I", + "I", + "L", + "L", + "J", + "L", + ) + instructions( + string("aa") + ) +} + +/** + * Identical to [legacyRenderBottomNavigationBarLegacyParentFingerprint] * except this has an extra parameter. */ -internal val renderBottomNavigationBarParentFingerprint = fingerprint { +internal val renderBottomNavigationBarLegacy1941ParentFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters( "I", @@ -57,7 +76,22 @@ internal val renderBottomNavigationBarParentFingerprint = fingerprint { "Ljava/lang/String;", "L", ) - strings("aa") + instructions( + string("aa") + ) +} + +internal val renderBottomNavigationBarParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("[Ljava/lang/Class;") + parameters( + "Ljava/lang/Class;", + "Ljava/lang/Object;", + "I" + ) + instructions( + string("RPCAC") + ) } internal val setPivotBarVisibilityFingerprint = fingerprint { @@ -72,23 +106,25 @@ internal val setPivotBarVisibilityFingerprint = fingerprint { internal val setPivotBarVisibilityParentFingerprint = fingerprint { parameters("Z") - strings("FEnotifications_inbox") + instructions( + string("FEnotifications_inbox") + ) } internal val shortsExperimentalPlayerFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") parameters() - literal { - 45677719L - } + instructions( + literal(45677719L) + ) } internal val renderNextUIFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") parameters() - literal { - 45649743L - } + instructions( + literal(45649743L) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt index de715d06c2..46e435f3f7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt @@ -8,9 +8,9 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch @@ -19,6 +19,8 @@ import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch import app.revanced.patches.youtube.misc.playservice.is_19_41_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_22_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_45_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch @@ -26,16 +28,11 @@ import app.revanced.util.findElementByAttributeValueOrThrow import app.revanced.util.forEachLiteralValueInstruction import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstLiteralInstruction import app.revanced.util.removeFromParent import app.revanced.util.returnLate import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference - -internal var bottomBarContainer = -1L - private set -internal var reelPlayerRightPivotV2Size = -1L - private set +import java.util.logging.Logger internal val hideShortsAppShortcutOption = booleanOption( key = "hideShortsAppShortcut", @@ -65,6 +62,69 @@ private val hideShortsComponentsResourcePatch = resourcePatch { addResources("youtube", "layout.hide.shorts.hideShortsComponentsResourcePatch") + val preferences = mutableSetOf( + // Shorts player components. + // Ideally each group should be ordered similar to how they appear in the UI + + // Vertical row of buttons on right side of the screen. + SwitchPreference("revanced_hide_shorts_like_fountain"), + SwitchPreference("revanced_hide_shorts_like_button"), + SwitchPreference("revanced_hide_shorts_dislike_button"), + ) + + if (is_20_22_or_greater) { + // FIXME: The buffer is very different for 20.22+ and these current cannot be hidden. + Logger.getLogger(this::class.java.name).warning( + "\n!!!" + + "\n!!! Shorts action buttons currently cannot be set hidden when patching 20.22+" + + "\n!!! Patch 20.21.37 or lower if you want to hide Shorts action buttons" + + "\n!!!" + ) + } else { + preferences.addAll( + listOf( + SwitchPreference("revanced_hide_shorts_comments_button"), + SwitchPreference("revanced_hide_shorts_share_button"), + SwitchPreference("revanced_hide_shorts_remix_button"), + SwitchPreference("revanced_hide_shorts_sound_button") + ) + ) + } + + preferences.addAll( + listOf( + // Upper and middle area of the player. + SwitchPreference("revanced_hide_shorts_join_button"), + SwitchPreference("revanced_hide_shorts_subscribe_button"), + SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"), + + // Suggested actions. + SwitchPreference("revanced_hide_shorts_preview_comment"), + SwitchPreference("revanced_hide_shorts_save_sound_button"), + SwitchPreference("revanced_hide_shorts_use_sound_button"), + SwitchPreference("revanced_hide_shorts_use_template_button"), + SwitchPreference("revanced_hide_shorts_upcoming_button"), + SwitchPreference("revanced_hide_shorts_effect_button"), + SwitchPreference("revanced_hide_shorts_green_screen_button"), + SwitchPreference("revanced_hide_shorts_hashtag_button"), + SwitchPreference("revanced_hide_shorts_new_posts_button"), + SwitchPreference("revanced_hide_shorts_shop_button"), + SwitchPreference("revanced_hide_shorts_tagged_products"), + SwitchPreference("revanced_hide_shorts_search_suggestions"), + SwitchPreference("revanced_hide_shorts_super_thanks_button"), + SwitchPreference("revanced_hide_shorts_stickers"), + + // Bottom of the screen. + SwitchPreference("revanced_hide_shorts_location_label"), + SwitchPreference("revanced_hide_shorts_channel_bar"), + SwitchPreference("revanced_hide_shorts_info_panel"), + SwitchPreference("revanced_hide_shorts_full_video_link_label"), + SwitchPreference("revanced_hide_shorts_video_title"), + SwitchPreference("revanced_hide_shorts_sound_metadata_label"), + SwitchPreference("revanced_hide_shorts_navigation_bar"), + ) + ) + PreferenceScreen.SHORTS.addPreferences( SwitchPreference("revanced_hide_shorts_home"), SwitchPreference("revanced_hide_shorts_search"), @@ -74,50 +134,8 @@ private val hideShortsComponentsResourcePatch = resourcePatch { PreferenceScreenPreference( key = "revanced_shorts_player_screen", sorting = PreferenceScreenPreference.Sorting.UNSORTED, - preferences = setOf( - // Shorts player components. - // Ideally each group should be ordered similar to how they appear in the UI - - // Vertical row of buttons on right side of the screen. - SwitchPreference("revanced_hide_shorts_like_fountain"), - SwitchPreference("revanced_hide_shorts_like_button"), - SwitchPreference("revanced_hide_shorts_dislike_button"), - SwitchPreference("revanced_hide_shorts_comments_button"), - SwitchPreference("revanced_hide_shorts_share_button"), - SwitchPreference("revanced_hide_shorts_remix_button"), - SwitchPreference("revanced_hide_shorts_sound_button"), - - // Upper and middle area of the player. - SwitchPreference("revanced_hide_shorts_join_button"), - SwitchPreference("revanced_hide_shorts_subscribe_button"), - SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"), - - // Suggested actions. - SwitchPreference("revanced_hide_shorts_preview_comment"), - SwitchPreference("revanced_hide_shorts_save_sound_button"), - SwitchPreference("revanced_hide_shorts_use_sound_button"), - SwitchPreference("revanced_hide_shorts_use_template_button"), - SwitchPreference("revanced_hide_shorts_upcoming_button"), - SwitchPreference("revanced_hide_shorts_effect_button"), - SwitchPreference("revanced_hide_shorts_green_screen_button"), - SwitchPreference("revanced_hide_shorts_hashtag_button"), - SwitchPreference("revanced_hide_shorts_new_posts_button"), - SwitchPreference("revanced_hide_shorts_shop_button"), - SwitchPreference("revanced_hide_shorts_tagged_products"), - SwitchPreference("revanced_hide_shorts_search_suggestions"), - SwitchPreference("revanced_hide_shorts_super_thanks_button"), - SwitchPreference("revanced_hide_shorts_stickers"), - - // Bottom of the screen. - SwitchPreference("revanced_hide_shorts_location_label"), - SwitchPreference("revanced_hide_shorts_channel_bar"), - SwitchPreference("revanced_hide_shorts_info_panel"), - SwitchPreference("revanced_hide_shorts_full_video_link_label"), - SwitchPreference("revanced_hide_shorts_video_title"), - SwitchPreference("revanced_hide_shorts_sound_metadata_label"), - SwitchPreference("revanced_hide_shorts_navigation_bar"), - ), - ), + preferences = preferences, + ) ) // Verify the file has the expected node, even if the patch option is off. @@ -142,16 +160,6 @@ private val hideShortsComponentsResourcePatch = resourcePatch { shortsItem.removeFromParent() } } - - bottomBarContainer = resourceMappings[ - "id", - "bottom_bar_container", - ] - - reelPlayerRightPivotV2Size = resourceMappings[ - "dimen", - "reel_player_right_pivot_v2_size", - ] } } @@ -173,10 +181,10 @@ val hideShortsComponentsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + // 20.22+ does not yet support hiding Shorts action buttons. ) ) @@ -187,7 +195,7 @@ val hideShortsComponentsPatch = bytecodePatch( addLithoFilter(FILTER_CLASS_DESCRIPTOR) forEachLiteralValueInstruction( - reelPlayerRightPivotV2Size, + getResourceId(ResourceType.DIMEN, "reel_player_right_pivot_v2_size") ) { literalInstructionIndex -> val targetIndex = indexOfFirstInstructionOrThrow(literalInstructionIndex) { getReference()?.name == "getDimensionPixelSize" @@ -213,7 +221,7 @@ val hideShortsComponentsPatch = bytecodePatch( setPivotBarVisibilityParentFingerprint.originalClassDef, ).let { result -> result.method.apply { - val insertIndex = result.patternMatch!!.endIndex + val insertIndex = result.instructionMatches.last().index val viewRegister = getInstruction(insertIndex - 1).registerA addInstruction( insertIndex, @@ -225,37 +233,37 @@ val hideShortsComponentsPatch = bytecodePatch( // Hook to hide the shared navigation bar when the Shorts player is opened. renderBottomNavigationBarFingerprint.match( - if (is_19_41_or_greater) { + (if (is_20_45_or_greater) { renderBottomNavigationBarParentFingerprint + } else if (is_19_41_or_greater) { + renderBottomNavigationBarLegacy1941ParentFingerprint } else { - legacyRenderBottomNavigationBarParentFingerprint - }.originalClassDef, + legacyRenderBottomNavigationBarLegacyParentFingerprint + }).originalClassDef ).method.addInstruction( 0, "invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V", ) // Hide the bottom bar container of the Shorts player. - shortsBottomBarContainerFingerprint.method.apply { - val resourceIndex = indexOfFirstLiteralInstruction(bottomBarContainer) - - val targetIndex = indexOfFirstInstructionOrThrow(resourceIndex) { - getReference()?.name == "getHeight" - } + 1 - - val heightRegister = getInstruction(targetIndex).registerA - - addInstructions( - targetIndex + 1, - """ - invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I - move-result v$heightRegister - """ - ) + shortsBottomBarContainerFingerprint.let { + it.method.apply { + val targetIndex = it.instructionMatches.last().index + val heightRegister = getInstruction(targetIndex).registerA + + addInstructions( + targetIndex + 1, + """ + invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I + move-result v$heightRegister + """ + ) + } } // endregion + // region Disable experimental Shorts flags. // Flags might be present in earlier targets, but they are not found in 19.47.53. diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/DisableSignInToTvPatchPopup.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/DisableSignInToTvPatchPopup.kt index 58af90ce2f..eefcf54c19 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/DisableSignInToTvPatchPopup.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/DisableSignInToTvPatchPopup.kt @@ -4,17 +4,12 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -internal var mdx_seamless_tv_sign_in_drawer_fragment_title_id = -1L - private set - private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisableSignInToTvPopupPatch;" @@ -31,10 +26,10 @@ val disableSignInToTvPopupPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -45,11 +40,6 @@ val disableSignInToTvPopupPatch = bytecodePatch( SwitchPreference("revanced_disable_signin_to_tv_popup"), ) - mdx_seamless_tv_sign_in_drawer_fragment_title_id = resourceMappings[ - "string", - "mdx_seamless_tv_sign_in_drawer_fragment_title", - ] - signInToTvPopupFingerprint.method.addInstructionsWithLabels( 0, """ diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/Fingerprints.kt index c79d4ed264..aabcdc4e5b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/signintotvpopup/Fingerprints.kt @@ -1,12 +1,16 @@ package app.revanced.patches.youtube.layout.hide.signintotvpopup import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral internal val signInToTvPopupFingerprint = fingerprint { returns("Z") parameters("Ljava/lang/String;", "Z", "L") - literal { - mdx_seamless_tv_sign_in_drawer_fragment_title_id - } + instructions( + resourceLiteral( + ResourceType.STRING, + "mdx_seamless_tv_sign_in_drawer_fragment_title" + ) + ) } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt deleted file mode 100644 index 388aee7ad6..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch - -@Deprecated("Use 'Hide suggested video end screen' instead.") -val disableSuggestedVideoEndScreenPatch = bytecodePatch { - dependsOn(hideEndScreenSuggestedVideoPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt index 1f1876dc30..16c64efb73 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt @@ -1,16 +1,33 @@ package app.revanced.patches.youtube.layout.hide.time +import app.revanced.patcher.InstructionLocation.* +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode internal val timeCounterFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters() - opcodes( - Opcode.SUB_LONG_2ADDR, - Opcode.IGET_WIDE, - Opcode.SUB_LONG_2ADDR + instructions( + opcode(Opcode.SUB_LONG_2ADDR), + methodCall( + opcode = Opcode.INVOKE_STATIC, + returnType = "Ljava/lang/CharSequence;", + location = MatchAfterImmediately() + ), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterImmediately()), + fieldAccess(opcode = Opcode.IGET_WIDE, type = "J", location = MatchAfterImmediately()), + fieldAccess(opcode = Opcode.IGET_WIDE, type = "J", location = MatchAfterImmediately()), + opcode(Opcode.SUB_LONG_2ADDR, location = MatchAfterImmediately()), + + methodCall( + opcode = Opcode.INVOKE_STATIC, + returnType = "Ljava/lang/CharSequence;", + location = MatchAfterWithin(5) + ) ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt index 0474929d50..eb646cb708 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt @@ -9,6 +9,8 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideTimestampPatch;" + val hideTimestampPatch = bytecodePatch( name = "Hide timestamp", description = "Adds an option to hide the timestamp in the bottom left of the video player.", @@ -21,10 +23,10 @@ val hideTimestampPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -38,13 +40,13 @@ val hideTimestampPatch = bytecodePatch( timeCounterFingerprint.method.addInstructionsWithLabels( 0, """ - invoke-static { }, Lapp/revanced/extension/youtube/patches/HideTimestampPatch;->hideTimestamp()Z + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->hideTimestamp()Z move-result v0 if-eqz v0, :hide_time return-void :hide_time nop - """, + """ ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt index 8c139aee4f..94d44fe6f1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt @@ -2,17 +2,52 @@ package app.revanced.patches.youtube.layout.miniplayer +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L +// In later targets this feature flag does nothing and is dead code. +internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L +internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L +internal const val MINIPLAYER_DRAG_DROP_FEATURE_KEY = 45628752L +internal const val MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY = 45658112L +internal const val MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY = 45652224L +internal const val MINIPLAYER_INITIAL_SIZE_FEATURE_KEY = 45640023L +internal const val MINIPLAYER_DISABLED_FEATURE_KEY = 45657015L +internal const val MINIPLAYER_ANIMATED_EXPAND_FEATURE_KEY = 45644360L + +internal val miniplayerModernConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + instructions( + literal(45623000L) // Magic number found in the constructor. + ) +} + internal val miniplayerDimensionsCalculatorParentFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("L") - literal { floatyBarButtonTopMargin } + instructions( + resourceLiteral(ResourceType.DIMEN, "floaty_bar_button_top_margin") + ) +} + +internal val miniplayerModernViewParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + parameters() + instructions( + string("player_overlay_modern_mini_player_controls") + ) } /** @@ -27,34 +62,14 @@ internal val miniplayerModernAddViewListenerFingerprint = fingerprint { /** * Matches using the class found in [miniplayerModernViewParentFingerprint]. */ - internal val miniplayerModernCloseButtonFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters() - literal { modernMiniplayerClose } -} - -internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L -// In later targets this feature flag does nothing and is dead code. -internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L -internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L -internal const val MINIPLAYER_DRAG_DROP_FEATURE_KEY = 45628752L -internal const val MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY = 45658112L -internal const val MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY = 45652224L -internal const val MINIPLAYER_INITIAL_SIZE_FEATURE_KEY = 45640023L -internal const val MINIPLAYER_DISABLED_FEATURE_KEY = 45657015L - -internal val miniplayerModernConstructorFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("L") - literal { 45623000L } -} - -internal val miniplayerOnCloseHandlerFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - literal { MINIPLAYER_DISABLED_FEATURE_KEY } + instructions( + resourceLiteral(ResourceType.ID, "modern_miniplayer_close"), + checkCast("Landroid/widget/ImageView;") + ) } /** @@ -64,7 +79,10 @@ internal val miniplayerModernExpandButtonFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters() - literal { modernMiniplayerExpand } + instructions( + resourceLiteral(ResourceType.ID, "modern_miniplayer_expand"), + checkCast("Landroid/widget/ImageView;") + ) } /** @@ -74,7 +92,9 @@ internal val miniplayerModernExpandCloseDrawablesFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("L") - literal { ytOutlinePictureInPictureWhite24 } + instructions( + literal(ytOutlinePictureInPictureWhite24) + ) } /** @@ -84,16 +104,19 @@ internal val miniplayerModernForwardButtonFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters() - literal { modernMiniplayerForwardButton } + instructions( + resourceLiteral(ResourceType.ID, "modern_miniplayer_forward_button"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterWithin(5)) + ) } -/** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. - */ internal val miniplayerModernOverlayViewFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters() - literal { scrimOverlay } + instructions( + resourceLiteral(ResourceType.ID, "scrim_overlay"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterWithin(5)) + ) } /** @@ -103,44 +126,58 @@ internal val miniplayerModernRewindButtonFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters() - literal { modernMiniplayerRewindButton } -} - -internal val miniplayerModernViewParentFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters() - strings("player_overlay_modern_mini_player_controls") + instructions( + resourceLiteral(ResourceType.ID, "modern_miniplayer_rewind_button"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterWithin(5)) + ) } +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ internal val miniplayerModernActionButtonFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters() - literal { modernMiniPlayerOverlayActionButton } + instructions( + resourceLiteral(ResourceType.ID, "modern_miniplayer_overlay_action_button"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterWithin(5)) + ) } internal val miniplayerMinimumSizeFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - custom { method, _ -> - method.containsLiteralInstruction(192) && - method.containsLiteralInstruction(128) && - method.containsLiteralInstruction(miniplayerMaxSize) - } + instructions( + resourceLiteral(ResourceType.DIMEN, "miniplayer_max_size"), + literal(192), // Default miniplayer width constant. + literal(128) // Default miniplayer height constant. + ) } internal val miniplayerOverrideFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") - strings("appName") + instructions( + string("appName"), + methodCall( + parameters = listOf("Landroid/content/Context;"), + returnType = "Z", + location = MatchAfterWithin(10) + ) + ) } internal val miniplayerOverrideNoContextFingerprint = fingerprint { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) returns("Z") - opcodes(Opcode.IGET_BOOLEAN) // Anchor to insert the instruction. + instructions( + opcode(Opcode.IGET_BOOLEAN) // Anchor to insert the instruction. + ) } +/** + * 20.36 and lower. Codes appears to be removed in 20.37+ + */ internal val miniplayerResponseModelSizeCheckFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") @@ -155,6 +192,14 @@ internal val miniplayerResponseModelSizeCheckFingerprint = fingerprint { ) } +internal val miniplayerOnCloseHandlerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + instructions( + literal(MINIPLAYER_DISABLED_FEATURE_KEY) + ) +} + internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME = "Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;" @@ -163,3 +208,12 @@ internal val playerOverlaysLayoutFingerprint = fingerprint { method.definingClass == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME } } + +internal val miniplayerSetIconsFingerprint = fingerprint { + returns("V") + parameters("I", "Ljava/lang/Runnable;") + instructions( + resourceLiteral(ResourceType.DRAWABLE, "yt_fill_pause_white_36"), + resourceLiteral(ResourceType.DRAWABLE, "yt_fill_pause_black_36") + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt index 2758210a5b..0cdba1e983 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt @@ -13,9 +13,9 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.* import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.* @@ -26,39 +26,18 @@ import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.TypeReference import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter -internal var floatyBarButtonTopMargin = -1L - private set - // Only available in 19.15 and upwards. internal var ytOutlineXWhite24 = -1L private set internal var ytOutlinePictureInPictureWhite24 = -1L private set -internal var scrimOverlay = -1L - private set -internal var modernMiniplayerClose = -1L - private set -internal var modernMiniplayerExpand = -1L - private set -internal var modernMiniplayerRewindButton = -1L - private set -internal var modernMiniplayerForwardButton = -1L - private set -internal var modernMiniPlayerOverlayActionButton = -1L - private set -internal var playerOverlays = -1L - private set -internal var miniplayerMaxSize = -1L - private set private val miniplayerResourcePatch = resourcePatch { dependsOn( @@ -67,72 +46,24 @@ private val miniplayerResourcePatch = resourcePatch { ) execute { - floatyBarButtonTopMargin = resourceMappings[ - "dimen", - "floaty_bar_button_top_margin", - ] - - scrimOverlay = resourceMappings[ - "id", - "scrim_overlay", - ] - - playerOverlays = resourceMappings[ - "layout", - "player_overlays", - ] - - - modernMiniplayerClose = resourceMappings[ - "id", - "modern_miniplayer_close", - ] - - modernMiniplayerExpand = resourceMappings[ - "id", - "modern_miniplayer_expand", - ] - - modernMiniplayerRewindButton = resourceMappings[ - "id", - "modern_miniplayer_rewind_button", - ] - - modernMiniplayerForwardButton = resourceMappings[ - "id", - "modern_miniplayer_forward_button", - ] - - modernMiniPlayerOverlayActionButton = resourceMappings[ - "id", - "modern_miniplayer_overlay_action_button" - ] - // Resource id is not used during patching, but is used by extension. // Verify the resource is present while patching. - resourceMappings[ - "id", + getResourceId( + ResourceType.ID, "modern_miniplayer_subtitle_text", - ] + ) // Only required for exactly 19.16 if (!is_19_17_or_greater) { - ytOutlinePictureInPictureWhite24 = resourceMappings[ - "drawable", + ytOutlinePictureInPictureWhite24 = getResourceId( + ResourceType.DRAWABLE, "yt_outline_picture_in_picture_white_24", - ] + ) - ytOutlineXWhite24 = resourceMappings[ - "drawable", + ytOutlineXWhite24 = getResourceId( + ResourceType.DRAWABLE, "yt_outline_x_white_24", - ] - } - - if (is_19_26_or_greater) { - miniplayerMaxSize = resourceMappings[ - "dimen", - "miniplayer_max_size", - ] + ) } } } @@ -153,10 +84,10 @@ val miniplayerPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -165,9 +96,16 @@ val miniplayerPatch = bytecodePatch( val preferences = mutableSetOf() + preferences += - if (is_20_03_or_greater) { + if (is_20_37_or_greater) { ListPreference("revanced_miniplayer_type") + } else if (is_20_03_or_greater) { + ListPreference( + key = "revanced_miniplayer_type", + entriesKey = "revanced_miniplayer_type_legacy_20_03_entries", + entryValuesKey = "revanced_miniplayer_type_legacy_20_03_entry_values" + ) } else if (is_19_43_or_greater) { ListPreference( key = "revanced_miniplayer_type", @@ -277,7 +215,7 @@ val miniplayerPatch = bytecodePatch( """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F move-result v$register - """, + """ ) } } @@ -293,51 +231,41 @@ val miniplayerPatch = bytecodePatch( """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I move-result v$register - """, - ) - } - - fun MutableMethod.hookInflatedView( - literalValue: Long, - hookedClassType: String, - extensionMethodName: String, - ) { - val imageViewIndex = indexOfFirstInstructionOrThrow( - indexOfFirstLiteralInstructionOrThrow(literalValue), - ) { - opcode == Opcode.CHECK_CAST && getReference()?.type == hookedClassType - } - - val register = getInstruction(imageViewIndex).registerA - addInstruction( - imageViewIndex + 1, - "invoke-static { v$register }, $extensionMethodName", + """ ) } // region Enable tablet miniplayer. + // Parts of the YT code is removed in 20.37+ and the legacy player no longer works. - miniplayerOverrideNoContextFingerprint.match( - miniplayerDimensionsCalculatorParentFingerprint.originalClassDef, - ).method.apply { - findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) } - } + if (!is_20_37_or_greater) { + miniplayerOverrideNoContextFingerprint.match( + miniplayerDimensionsCalculatorParentFingerprint.originalClassDef, + ).method.apply { + findReturnIndicesReversed().forEach { index -> + insertLegacyTabletMiniplayerOverride( + index + ) + } + } - // endregion + // endregion - // region Legacy tablet miniplayer hooks. - val appNameStringIndex = miniplayerOverrideFingerprint.let { - it.method.indexOfFirstInstructionOrThrow(it.stringMatches!!.first().index) { - val reference = getReference() - reference?.parameterTypes?.firstOrNull() == "Landroid/content/Context;" + // region Legacy tablet miniplayer hooks. + miniplayerOverrideFingerprint.let { + val appNameStringIndex = it.instructionMatches.last().index + navigate(it.originalMethod).to(appNameStringIndex).stop().apply { + findReturnIndicesReversed().forEach { index -> + insertLegacyTabletMiniplayerOverride( + index + ) + } + } } - } - navigate(miniplayerOverrideFingerprint.originalMethod).to(appNameStringIndex).stop().apply { - findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) } - } - miniplayerResponseModelSizeCheckFingerprint.let { - it.method.insertLegacyTabletMiniplayerOverride(it.patternMatch!!.endIndex) + miniplayerResponseModelSizeCheckFingerprint.let { + it.method.insertLegacyTabletMiniplayerOverride(it.instructionMatches.last().index) + } } // endregion @@ -388,7 +316,6 @@ val miniplayerPatch = bytecodePatch( MINIPLAYER_INITIAL_SIZE_FEATURE_KEY, ) val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT) - val register = getInstruction(targetIndex).registerA addInstructions( @@ -396,21 +323,21 @@ val miniplayerPatch = bytecodePatch( """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getMiniplayerDefaultSize(I)I move-result v$register - """, + """ ) } // Override a minimum size constant. - miniplayerMinimumSizeFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow { - opcode == Opcode.CONST_16 && (this as NarrowLiteralInstruction).narrowLiteral == 192 + miniplayerMinimumSizeFingerprint.let { + it.method.apply { + val index = it.instructionMatches[1].index + val register = getInstruction(index).registerA + + // Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller. + // The 170 initial limit probably could be patched to allow even smaller initial sizes, + // but 170 is already half the horizontal space and smaller does not seem useful. + replaceInstruction(index, "const/16 v$register, 170") } - val register = getInstruction(index).registerA - - // Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller. - // The 170 initial limit probably could be patched to allow even smaller initial sizes, - // but 170 is already half the horizontal space and smaller does not seem useful. - replaceInstruction(index, "const/16 v$register, 170") } } @@ -431,6 +358,11 @@ val miniplayerPatch = bytecodePatch( MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY, "getHorizontalDrag", ) + + miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + MINIPLAYER_ANIMATED_EXPAND_FEATURE_KEY, + "getMaximizeAnimation", + ) } // endregion @@ -438,7 +370,7 @@ val miniplayerPatch = bytecodePatch( // region Fix 19.16 using mixed up drawables for tablet modern. // YT fixed this mistake in 19.17. // Fix this, by swapping the drawable resource values with each other. - if (ytOutlinePictureInPictureWhite24 >= 0) { + if (!is_19_17_or_greater) { miniplayerModernExpandCloseDrawablesFingerprint.match( miniplayerModernViewParentFingerprint.originalClassDef, ).method.apply { @@ -456,58 +388,49 @@ val miniplayerPatch = bytecodePatch( // endregion + // region fix minimal miniplayer using the wrong pause/play bold icons. + + if (is_20_31_or_greater) { + miniplayerSetIconsFingerprint.method.apply { + findInstructionIndicesReversedOrThrow { + val reference = getReference() + opcode == Opcode.INVOKE_INTERFACE + && reference?.returnType == "Z" && reference.parameterTypes.isEmpty() + }.forEach { index -> + val register = getInstruction(index + 1).registerA + + addInstructions( + index + 2, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->allowBoldIcons(Z)Z + move-result v$register + """ + ) + } + } + } + + // endregion + // region Add hooks to hide modern miniplayer buttons. listOf( - Triple( - miniplayerModernExpandButtonFingerprint, - modernMiniplayerExpand, - "hideMiniplayerExpandClose", - ), - Triple( - miniplayerModernCloseButtonFingerprint, - modernMiniplayerClose, - "hideMiniplayerExpandClose", - ), - Triple( - miniplayerModernActionButtonFingerprint, - modernMiniPlayerOverlayActionButton, - "hideMiniplayerActionButton" - ), - Triple( - miniplayerModernRewindButtonFingerprint, - modernMiniplayerRewindButton, - "hideMiniplayerRewindForward", - ), - Triple( - miniplayerModernForwardButtonFingerprint, - modernMiniplayerForwardButton, - "hideMiniplayerRewindForward", - ), - Triple( - miniplayerModernOverlayViewFingerprint, - scrimOverlay, - "adjustMiniplayerOpacity", - ), - ).forEach { (fingerprint, literalValue, methodName) -> + miniplayerModernExpandButtonFingerprint to "hideMiniplayerExpandClose", + miniplayerModernCloseButtonFingerprint to "hideMiniplayerExpandClose", + miniplayerModernActionButtonFingerprint to "hideMiniplayerActionButton", + miniplayerModernRewindButtonFingerprint to "hideMiniplayerRewindForward", + miniplayerModernForwardButtonFingerprint to "hideMiniplayerRewindForward", + miniplayerModernOverlayViewFingerprint to "adjustMiniplayerOpacity" + ).forEach { (fingerprint, methodName) -> fingerprint.match( - miniplayerModernViewParentFingerprint.originalClassDef + miniplayerModernViewParentFingerprint.classDef, ).method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(literalValue) - val checkCastIndex = indexOfFirstInstruction(literalIndex) { - opcode == Opcode.CHECK_CAST && - getReference()?.type == "Landroid/widget/ImageView;" - } - val viewIndex = if (checkCastIndex >= 0) { - checkCastIndex - } else { - indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT_OBJECT) - } - val viewRegister = getInstruction(viewIndex).registerA + val index = fingerprint.instructionMatches.last().index + val register = getInstruction(index).registerA addInstruction( - viewIndex + 1, - "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V" + index + 1, + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V", ) } } @@ -550,10 +473,10 @@ val miniplayerPatch = bytecodePatch( ).toMutable().apply { addInstructions( """ - invoke-super { p0, p1, p2, p3 }, Landroid/view/ViewGroup;->addView(Landroid/view/View;ILandroid/view/ViewGroup${'$'}LayoutParams;)V - invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->playerOverlayGroupCreated(Landroid/view/View;)V - return-void - """ + invoke-super { p0, p1, p2, p3 }, Landroid/view/ViewGroup;->addView(Landroid/view/View;ILandroid/view/ViewGroup${'$'}LayoutParams;)V + invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->playerOverlayGroupCreated(Landroid/view/View;)V + return-void + """ ) } ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt index 0c31cc83bd..1f7f033920 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt @@ -1,10 +1,8 @@ package app.revanced.patches.youtube.layout.panels.popup -import com.android.tools.smali.dexlib2.AccessFlags import app.revanced.patcher.fingerprint internal val engagementPanelControllerFingerprint = fingerprint { - accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) returns("L") strings( "EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt index 518b3b04c9..62fd44072b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt @@ -9,6 +9,8 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisablePlayerPopupPanelsPatch;" + val playerPopupPanelsPatch = bytecodePatch( name = "Disable player popup panels", description = "Adds an option to disable panels (such as live chat) from opening automatically.", @@ -21,10 +23,10 @@ val playerPopupPanelsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -38,7 +40,7 @@ val playerPopupPanelsPatch = bytecodePatch( engagementPanelControllerFingerprint.method.addInstructionsWithLabels( 0, """ - invoke-static { }, Lapp/revanced/extension/youtube/patches/DisablePlayerPopupPanelsPatch;->disablePlayerPopupPanels()Z + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disablePlayerPopupPanels()Z move-result v0 if-eqz v0, :player_popup_panels if-eqz p4, :player_popup_panels @@ -46,7 +48,7 @@ val playerPopupPanelsPatch = bytecodePatch( return-object v0 :player_popup_panels nop - """, + """ ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt deleted file mode 100644 index 5c20556a85..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt +++ /dev/null @@ -1,12 +0,0 @@ -package app.revanced.patches.youtube.layout.player.background - -import app.revanced.patcher.patch.resourcePatch -import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch - -@Suppress("unused") -@Deprecated("Functionality added to hidePlayerOverlayButtonsPatch", ReplaceWith("hidePlayerOverlayButtonsPatch")) -val playerControlsBackgroundPatch = resourcePatch( - description = "Removes the dark background surrounding the video player control buttons.", -) { - dependsOn(hidePlayerOverlayButtonsPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/ExitFullscreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/ExitFullscreenPatch.kt index 42babd5ce5..2a8f07370e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/ExitFullscreenPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/ExitFullscreenPatch.kt @@ -9,9 +9,11 @@ import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.patches.youtube.shared.loopVideoFingerprint -import app.revanced.patches.youtube.shared.loopVideoParentFingerprint +import app.revanced.patches.youtube.video.information.videoEndMethod +import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import com.android.tools.smali.dexlib2.Opcode @Suppress("unused") internal val exitFullscreenPatch = bytecodePatch( @@ -21,10 +23,10 @@ internal val exitFullscreenPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -33,7 +35,8 @@ internal val exitFullscreenPatch = bytecodePatch( settingsPatch, addResourcesPatch, playerTypeHookPatch, - playerControlsPatch + playerControlsPatch, + videoInformationPatch ) // Cannot declare as top level since this patch is in the same package as @@ -49,9 +52,11 @@ internal val exitFullscreenPatch = bytecodePatch( ListPreference("revanced_exit_fullscreen") ) - loopVideoFingerprint.match(loopVideoParentFingerprint.originalClassDef).method.apply { + videoEndMethod.apply { + val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.RETURN_VOID) + addInstructionsAtControlFlowLabel( - implementation!!.instructions.lastIndex, + insertIndex, "invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->endOfVideoReached()V", ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt index a8aef37341..aecf9e228a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt @@ -1,18 +1,46 @@ package app.revanced.patches.youtube.layout.player.fullscreen +import app.revanced.patcher.InstructionLocation.MatchAfterWithin import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.opcode import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode -internal const val OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG = 45666112L - +/** + * 19.46+ + */ internal val openVideosFullscreenPortraitFingerprint = fingerprint { + returns("V") + parameters("L", "Lj\$/util/Optional;") + instructions( + opcode(Opcode.MOVE_RESULT), // Conditional check to modify. + // Open videos fullscreen portrait feature flag. + literal(45666112L, location = MatchAfterWithin(5)), // Cannot be more than 5. + opcode(Opcode.MOVE_RESULT, location = MatchAfterWithin(10)), + ) +} + +/** + * Pre 19.46. + */ +internal val openVideosFullscreenPortraitLegacyFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("L", "Lj\$/util/Optional;") - literal { - OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG - } + opcodes( + Opcode.GOTO, + Opcode.SGET_OBJECT, + Opcode.GOTO, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQ, + Opcode.IF_EQ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT // Conditional check to modify. + ) } internal val openVideosFullscreenHookPatchExtensionFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreen.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreen.kt deleted file mode 100644 index 89311b08c0..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreen.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.youtube.layout.player.fullscreen - -import app.revanced.patcher.patch.bytecodePatch - -@Suppress("unused") -@Deprecated("Renamed to openVideosFullscreenPatch", ReplaceWith("openVideosFullscreenPatch")) -val openVideosFullscreen = bytecodePatch{ - dependsOn(openVideosFullscreenPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenHookPatch.kt index 76086c0e48..79253d46b6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenHookPatch.kt @@ -1,11 +1,15 @@ package app.revanced.patches.youtube.layout.player.fullscreen +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlayerPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.util.insertLiteralOverride +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;" @@ -20,13 +24,40 @@ internal val openVideosFullscreenHookPatch = bytecodePatch { ) execute { - if (!is_19_46_or_greater) { - return@execute + var fingerprint: Fingerprint + var insertIndex: Int + + if (is_19_46_or_greater) { + fingerprint = openVideosFullscreenPortraitFingerprint + insertIndex = fingerprint.instructionMatches.first().index + + openVideosFullscreenPortraitFingerprint.let { + // Remove A/B feature call that forces what this patch already does. + // Cannot use the A/B flag to accomplish the same goal because 19.50+ + // Shorts fullscreen regular player does not use fullscreen + // if the player is minimized and it must be forced using other conditional check. + it.method.insertLiteralOverride( + it.instructionMatches.last().index, + false + ) + } + } else { + fingerprint = openVideosFullscreenPortraitLegacyFingerprint + insertIndex = fingerprint.instructionMatches.last().index } - openVideosFullscreenPortraitFingerprint.method.insertLiteralOverride( - OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z" - ) + fingerprint.let { + it.method.apply { + val register = getInstruction(insertIndex).registerA + + addInstructions( + insertIndex + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->doNotOpenVideoFullscreenPortrait(Z)Z + move-result v$register + """ + ) + } + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatch.kt index a0b513f3e1..2a3e7d4831 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatch.kt @@ -1,12 +1,9 @@ package app.revanced.patches.youtube.layout.player.fullscreen -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater -import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.util.returnEarly @@ -20,22 +17,20 @@ val openVideosFullscreenPatch = bytecodePatch( openVideosFullscreenHookPatch, settingsPatch, addResourcesPatch, - versionCheckPatch ) compatibleWith( "com.google.android.youtube"( - "20.07.39", - "20.13.41", + "19.43.41", + "19.43.41", + "19.47.53", "20.14.43", + "20.21.37", + "20.31.40", ) ) execute { - if (!is_19_46_or_greater) { - throw PatchException("'Open videos fullscreen' requires 19.46.42 or greater") - } - addResources("youtube", "layout.player.fullscreen.openVideosFullscreen") PreferenceScreen.PLAYER.addPreferences( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt index c6229e259f..766b2c865b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt @@ -3,43 +3,15 @@ package app.revanced.patches.youtube.layout.player.overlay import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.InputType import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -internal var scrimOverlayId = -1L - private set - -private val customPlayerOverlayOpacityResourcePatch = resourcePatch { - dependsOn( - settingsPatch, - resourceMappingPatch, - addResourcesPatch, - ) - - execute { - addResources("youtube", "layout.player.overlay.customPlayerOverlayOpacityResourcePatch") - - PreferenceScreen.PLAYER.addPreferences( - TextPreference("revanced_player_overlay_opacity", inputType = InputType.NUMBER), - ) - - scrimOverlayId = resourceMappings[ - "id", - "scrim_overlay", - ] - } -} - private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/CustomPlayerOverlayOpacityPatch;" @@ -48,30 +20,38 @@ val customPlayerOverlayOpacityPatch = bytecodePatch( name = "Custom player overlay opacity", description = "Adds an option to change the opacity of the video player background when player controls are visible.", ) { - dependsOn(customPlayerOverlayOpacityResourcePatch) + dependsOn(settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) execute { - createPlayerOverviewFingerprint.method.apply { - val viewRegisterIndex = - indexOfFirstLiteralInstructionOrThrow(scrimOverlayId) + 3 - val viewRegister = - getInstruction(viewRegisterIndex).registerA + addResources("youtube", "layout.player.overlay.customPlayerOverlayOpacityResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + TextPreference("revanced_player_overlay_opacity", inputType = InputType.NUMBER), + ) - val insertIndex = viewRegisterIndex + 1 - addInstruction( - insertIndex, - "invoke-static { v$viewRegister }, " + - "$EXTENSION_CLASS_DESCRIPTOR->changeOpacity(Landroid/widget/ImageView;)V", - ) + createPlayerOverviewFingerprint.let { + it.method.apply { + val viewRegisterIndex = it.instructionMatches.last().index + val viewRegister = getInstruction(viewRegisterIndex).registerA + + addInstruction( + viewRegisterIndex + 1, + "invoke-static { v$viewRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->changeOpacity(Landroid/widget/ImageView;)V", + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt index 7269868402..e8db1e6512 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt @@ -1,18 +1,15 @@ package app.revanced.patches.youtube.layout.player.overlay +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint -import app.revanced.util.literal -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral internal val createPlayerOverviewFingerprint = fingerprint { - accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) returns("V") - opcodes( - Opcode.CONST, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CHECK_CAST, + instructions( + resourceLiteral(ResourceType.ID, "scrim_overlay"), + checkCast("Landroid/widget/ImageView;", location = MatchAfterWithin(10)) ) - literal { scrimOverlayId } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt index 54fda75c85..a138771d4b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt @@ -1,23 +1,30 @@ package app.revanced.patches.youtube.layout.returnyoutubedislike import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode internal val dislikeFingerprint = fingerprint { returns("V") - strings("like/dislike") + instructions( + string("like/dislike") + ) } internal val likeFingerprint = fingerprint { returns("V") - strings("like/like") + instructions( + string("like/like") + ) } internal val removeLikeFingerprint = fingerprint { returns("V") - strings("like/removelike") + instructions( + string("like/removelike") + ) } internal val rollingNumberMeasureAnimatedTextFingerprint = fingerprint { @@ -57,7 +64,9 @@ internal val rollingNumberMeasureStaticLabelParentFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Ljava/lang/String;") parameters() - strings("RollingNumberFontProperties{paint=") + instructions( + string("RollingNumberFontProperties{paint=") + ) } internal val rollingNumberSetterFingerprint = fingerprint { @@ -89,13 +98,17 @@ internal val rollingNumberTextViewFingerprint = fingerprint { internal val textComponentConstructorFingerprint = fingerprint { accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PRIVATE) - strings("TextComponent") + instructions( + string("TextComponent") + ) } internal val textComponentDataFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) parameters("L", "L") - strings("text") + instructions( + string("text") + ) custom { _, classDef -> classDef.fields.find { it.type == "Ljava/util/BitSet;" } != null } @@ -108,14 +121,16 @@ internal val textComponentLookupFingerprint = fingerprint { accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) returns("L") parameters("L") - strings("…") + instructions( + string("…") + ) } -internal const val LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG = 45675738L - internal val textComponentFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.FINAL) returns("Z") parameters() - literal { LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG } -} + instructions ( + literal(45675738L) + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt index a73570920b..bd57e0af1a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt @@ -17,6 +17,7 @@ import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_41_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch @@ -38,6 +39,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.TypeReference +import java.util.logging.Logger private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch;" @@ -61,10 +63,11 @@ val returnYouTubeDislikePatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", + // 20.40+ does not support yet support the Shorts player. ) ) @@ -102,7 +105,7 @@ val returnYouTubeDislikePatch = bytecodePatch( // region Hook like/dislike/remove like button clicks to send votes to the API. - mapOf( + arrayOf( likeFingerprint to Vote.LIKE, dislikeFingerprint to Vote.DISLIKE, removeLikeFingerprint to Vote.REMOVE_LIKE, @@ -120,64 +123,70 @@ val returnYouTubeDislikePatch = bytecodePatch( // region Hook code for creation and cached lookup of text Spans. - // Alternatively the hook can be made at tht it fails to update the Span when the user dislikes, - // // since the underlying (likes only) tee creation of Spans in TextComponentSpec, - // And it works in all situations excepxt did not change. + // Alternatively the hook can be made in the creation of Spans in TextComponentSpec. + // And it works in all situations except if the likes do not such as disliking. // This hook handles all situations, as it's where the created Spans are stored and later reused. + // Find the field name of the conversion context. - val conversionContextField = textComponentConstructorFingerprint.originalClassDef.fields.find { - it.type == conversionContextFingerprintToString.originalClassDef.type + val conversionContextClass = conversionContextFingerprintToString.originalClassDef + val textComponentConversionContextField = textComponentConstructorFingerprint.originalClassDef.fields.find { + it.type == conversionContextClass.type + // 20.41+ uses superclass field type. + || it.type == conversionContextClass.superclass } ?: throw PatchException("Could not find conversion context field") - textComponentLookupFingerprint.match(textComponentConstructorFingerprint.originalClassDef) - .method.apply { + textComponentLookupFingerprint.match(textComponentConstructorFingerprint.originalClassDef).method.apply { // Find the instruction for creating the text data object. val textDataClassType = textComponentDataFingerprint.originalClassDef.type val insertIndex: Int - val tempRegister: Int val charSequenceRegister: Int - if (is_19_33_or_greater && !is_20_10_or_greater) { - insertIndex = indexOfFirstInstructionOrThrow { + if (is_19_33_or_greater && !is_20_10_or_greater) { + val index = indexOfFirstInstructionOrThrow { (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) && getReference()?.returnType == textDataClassType } - tempRegister = getInstruction(insertIndex + 1).registerA - - // Find the instruction that sets the span to an instance field. - // The instruction is only a few lines after the creation of the instance. - charSequenceRegister = getInstruction( - indexOfFirstInstructionOrThrow(insertIndex) { - opcode == Opcode.INVOKE_VIRTUAL && + insertIndex = indexOfFirstInstructionOrThrow(index) { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;" - }, - ).registerD + } + + charSequenceRegister = getInstruction(insertIndex).registerD } else { insertIndex = indexOfFirstInstructionOrThrow { opcode == Opcode.NEW_INSTANCE && getReference()?.type == textDataClassType } - tempRegister = getInstruction(insertIndex).registerA - - charSequenceRegister = getInstruction( - indexOfFirstInstructionOrThrow(insertIndex) { - opcode == Opcode.IPUT_OBJECT && + val charSequenceIndex = indexOfFirstInstructionOrThrow(insertIndex) { + opcode == Opcode.IPUT_OBJECT && getReference()?.type == "Ljava/lang/CharSequence;" - }, - ).registerA + } + charSequenceRegister = getInstruction(charSequenceIndex).registerA } + val free1 = findFreeRegister(insertIndex, charSequenceRegister) + val free2 = findFreeRegister(insertIndex, charSequenceRegister, free1) + addInstructionsAtControlFlowLabel( insertIndex, """ - # Copy conversion context - move-object/from16 v$tempRegister, p0 - iget-object v$tempRegister, v$tempRegister, $conversionContextField - invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; + # Copy conversion context. + move-object/from16 v$free1, p0 + + # 20.41 field is the abstract superclass. + # Verify it's the expected subclass just in case. + instance-of v$free2, v$free1, ${textComponentConversionContextField.type} + if-eqz v$free2, :ignore + + check-cast v$free1, $conversionContextClass + invoke-static { v$free1, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; move-result-object v$charSequenceRegister + + :ignore + nop """ ) } @@ -194,7 +203,22 @@ val returnYouTubeDislikePatch = bytecodePatch( // If enabled then the litho text span hook is never called. // Target code is very obfuscated and exactly what the code does is not clear. // Return late so debug patch logs if the flag is enabled. - textComponentFeatureFlagFingerprint.method.returnLate(false) + if (is_20_41_or_greater) { + // TODO: Support the new non litho Shorts layout. + // Turning off this flag on later versions can break the Shorts overlay and nothing is shown. + Logger.getLogger(this::class.java.name).warning( + "\n!!!" + + "\n!!! Dislikes are not yet fully supported when patching YouTube 20.40+" + + "\n!!! Patch 20.21.37 or lower if you want to see dislikes" + + "\n!!!" + ) + + Logger.getLogger(this::class.java.name).warning( + "20.40+ Shorts player is not fully supported yet. Shorts Dislikes may not show." + ) + } else { + textComponentFeatureFlagFingerprint.method.returnLate(false) + } } // Player response video id is needed to search for the video ids in Shorts litho components. @@ -204,11 +228,9 @@ val returnYouTubeDislikePatch = bytecodePatch( // region Hook rolling numbers. - val dislikesIndex = rollingNumberSetterFingerprint.patternMatch!!.endIndex - rollingNumberSetterFingerprint.method.apply { val insertIndex = 1 - + val dislikesIndex = rollingNumberSetterFingerprint.instructionMatches.last().index val charSequenceInstanceRegister = getInstruction(0).registerA val charSequenceFieldReference = @@ -225,17 +247,16 @@ val returnYouTubeDislikePatch = bytecodePatch( invoke-static {v$conversionContextRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->onRollingNumberLoaded(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String; move-result-object v$freeRegister iput-object v$freeRegister, v$charSequenceInstanceRegister, $charSequenceFieldReference - """, + """ ) } + // Rolling Number text views use the measured width of the raw string for layout. + // Modify the measure text calculation to include the left drawable separator if needed. rollingNumberMeasureAnimatedTextFingerprint.let { - // Rolling Number text views use the measured width of the raw string for layout. - // Modify the measure text calculation to include the left drawable separator if needed. - val patternMatch = it.patternMatch!! - // Verify the opcodes are at the start of the method. - if (patternMatch.startIndex != 0) throw PatchException("Unexpected opcode location") - val endIndex = patternMatch.endIndex + // Additional check to verify the opcodes are at the start of the method + if (it.instructionMatches.first().index != 0) throw PatchException("Unexpected opcode location") + val endIndex = it.instructionMatches.last().index it.method.apply { val measuredTextWidthRegister = getInstruction(endIndex).registerA @@ -255,7 +276,7 @@ val returnYouTubeDislikePatch = bytecodePatch( rollingNumberMeasureStaticLabelFingerprint.match( rollingNumberMeasureStaticLabelParentFingerprint.originalClassDef, ).let { - val measureTextIndex = it.patternMatch!!.startIndex + 1 + val measureTextIndex = it.instructionMatches.first().index + 1 it.method.apply { val freeRegister = getInstruction(0).registerA @@ -264,7 +285,7 @@ val returnYouTubeDislikePatch = bytecodePatch( """ move-result v$freeRegister invoke-static {p1, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F - """, + """ ) } } @@ -283,10 +304,8 @@ val returnYouTubeDislikePatch = bytecodePatch( getReference()?.name == "setText" } - val textViewRegister = - getInstruction(setTextIndex).registerC - val textSpanRegister = - getInstruction(setTextIndex).registerD + val textViewRegister = getInstruction(setTextIndex).registerC + val textSpanRegister = getInstruction(setTextIndex).registerD addInstructions( setTextIndex, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt index 2dbff83a23..53e14e1f41 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt @@ -1,19 +1,19 @@ package app.revanced.patches.youtube.layout.searchbar import app.revanced.patcher.fingerprint +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import app.revanced.patches.youtube.layout.hide.general.yoodlesImageViewFingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags internal val setWordmarkHeaderFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("Landroid/widget/ImageView;") - custom { methodDef, _ -> - methodDef.containsLiteralInstruction(ytPremiumWordmarkHeaderId) && - methodDef.containsLiteralInstruction(ytWordmarkHeaderId) - } + instructions( + resourceLiteral(ResourceType.ATTR, "ytPremiumWordmarkHeader"), + resourceLiteral(ResourceType.ATTR, "ytWordmarkHeader") + ) } /** @@ -23,5 +23,7 @@ internal val wideSearchbarLayoutFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Landroid/view/View;") parameters("L", "L") - literal { actionBarRingoId } + instructions( + resourceLiteral(ResourceType.LAYOUT, "action_bar_ringo"), + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt index 91c827f5d6..1c32cd589e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt @@ -3,14 +3,13 @@ package app.revanced.patches.youtube.layout.searchbar import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_20_31_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.util.addInstructionsAtControlFlowLabel @@ -20,38 +19,11 @@ import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import java.util.logging.Logger private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/WideSearchbarPatch;" -internal var ytWordmarkHeaderId = -1L - private set -internal var ytPremiumWordmarkHeaderId = -1L - private set -internal var actionBarRingoId = -1L - private set - -private val wideSearchbarResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - - execute { - ytWordmarkHeaderId = resourceMappings[ - "attr", - "ytWordmarkHeader", - ] - - ytPremiumWordmarkHeaderId = resourceMappings[ - "attr", - "ytPremiumWordmarkHeader", - ] - - actionBarRingoId = resourceMappings[ - "layout", - "action_bar_ringo", - ] - } -} - val wideSearchbarPatch = bytecodePatch( name = "Wide search bar", description = "Adds an option to replace the search icon with a wide search bar. " + @@ -61,19 +33,29 @@ val wideSearchbarPatch = bytecodePatch( sharedExtensionPatch, settingsPatch, addResourcesPatch, - wideSearchbarResourcePatch, + resourceMappingPatch, + versionCheckPatch ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + // 20.31.40+ not supported. YouTube code was removed. ) ) execute { + if (is_20_31_or_greater) { + // YT removed the legacy text search text field all code required to use it. + // This functionality could be restored by adding a search text field to the toolbar + // with a listener that artificially clicks the toolbar search button. + return@execute Logger.getLogger(this::class.java.name).warning( + "Wide searchbar is not compatible with 20.31+" + ) + } + addResources("youtube", "layout.searchbar.wideSearchbarPatch") PreferenceScreen.FEED.addPreferences( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt index 24e062d408..5014126efa 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt @@ -1,67 +1,75 @@ package app.revanced.patches.youtube.layout.seekbar +import app.revanced.patcher.InstructionLocation.MatchAfterImmediately +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.anyInstruction import app.revanced.patcher.fingerprint +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import app.revanced.patches.youtube.shared.YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal val fullscreenSeekbarThumbnailsFingerprint = fingerprint { returns("Z") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters() - literal { 45398577 } + instructions( + literal(45398577) + ) } internal val playerSeekbarColorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - custom { method, _ -> - method.containsLiteralInstruction(inlineTimeBarColorizedBarPlayedColorDarkId) && - method.containsLiteralInstruction(inlineTimeBarPlayedNotHighlightedColorId) - } + instructions( + resourceLiteral(ResourceType.COLOR, "inline_time_bar_played_not_highlighted_color"), + resourceLiteral(ResourceType.COLOR, "inline_time_bar_colorized_bar_played_color_dark") + ) } +// class is ControlsOverlayStyle in 20.32 and lower, and obfuscated in 20.33+ internal val setSeekbarClickedColorFingerprint = fingerprint { opcodes(Opcode.CONST_HIGH16) - strings("YOUTUBE", "PREROLL", "POSTROLL") - custom { _, classDef -> - classDef.endsWith("ControlsOverlayStyle;") - } + strings("YOUTUBE", "PREROLL", "POSTROLL", "REMOTE_LIVE", "AD_LARGE_CONTROLS") } internal val shortsSeekbarColorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - literal { reelTimeBarPlayedColorId } + instructions( + resourceLiteral(ResourceType.COLOR, "reel_time_bar_played_color") + ) } internal val playerSeekbarHandle1ColorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("Landroid/content/Context;") - custom { method, _ -> - method.containsLiteralInstruction(ytTextSecondaryId) && - method.containsLiteralInstruction(ytStaticBrandRedId) - } + instructions( + resourceLiteral(ResourceType.COLOR, "inline_time_bar_live_seekable_range"), + resourceLiteral(ResourceType.ATTR, "ytStaticBrandRed"), + ) } internal val playerSeekbarHandle2ColorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) parameters("Landroid/content/Context;") - custom { method, _ -> - method.containsLiteralInstruction(inlineTimeBarLiveSeekableRangeId) && - method.containsLiteralInstruction(ytStaticBrandRedId) - } + instructions( + resourceLiteral(ResourceType.ATTR, "ytTextSecondary"), + resourceLiteral(ResourceType.ATTR, "ytStaticBrandRed"), + ) } - internal val watchHistoryMenuUseProgressDrawableFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("L") - literal { -1712394514 } + instructions( + methodCall("Landroid/widget/ProgressBar;", "setMax"), + opcode(Opcode.MOVE_RESULT), + literal(-1712394514) + ) } internal val lithoLinearGradientFingerprint = fingerprint { @@ -77,11 +85,12 @@ internal val playerLinearGradientFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) parameters("I", "I", "I", "I", "Landroid/content/Context;", "I") returns("Landroid/graphics/LinearGradient;") - opcodes( - Opcode.FILLED_NEW_ARRAY, - Opcode.MOVE_RESULT_OBJECT + instructions( + resourceLiteral(ResourceType.COLOR, "yt_youtube_magenta"), + + opcode(Opcode.FILLED_NEW_ARRAY, location = MatchAfterWithin(5)), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterImmediately()) ) - literal { ytYoutubeMagentaColorId } } /** @@ -89,25 +98,12 @@ internal val playerLinearGradientFingerprint = fingerprint { */ internal val playerLinearGradientLegacyFingerprint = fingerprint { returns("V") - opcodes( - Opcode.FILLED_NEW_ARRAY, - Opcode.MOVE_RESULT_OBJECT - ) - literal { ytYoutubeMagentaColorId } -} + instructions( + resourceLiteral(ResourceType.COLOR, "yt_youtube_magenta"), -internal const val launchScreenLayoutTypeLotteFeatureFlag = 268507948L - -internal val launchScreenLayoutTypeFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - custom { method, _ -> - val firstParameter = method.parameterTypes.firstOrNull() - // 19.25 - 19.45 - (firstParameter == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE - || firstParameter == "Landroid/app/Activity;") // 19.46+ - && method.containsLiteralInstruction(launchScreenLayoutTypeLotteFeatureFlag) - } + opcode(Opcode.FILLED_NEW_ARRAY), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterImmediately()), + ) } internal const val LOTTIE_ANIMATION_VIEW_CLASS_TYPE = "Lcom/airbnb/lottie/LottieAnimationView;" @@ -116,33 +112,22 @@ internal val lottieAnimationViewSetAnimationIntFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters("I") returns("V") - custom { methodDef, classDef -> - classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && methodDef.indexOfFirstInstruction { - val reference = getReference() - reference?.definingClass == "Lcom/airbnb/lottie/LottieAnimationView;" - && reference.name == "isInEditMode" - } >= 0 - } -} - -internal val lottieAnimationViewSetAnimationStreamFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("L") - returns("V") - custom { methodDef, classDef -> - classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && methodDef.indexOfFirstInstruction { - val reference = getReference() - reference?.definingClass == "Ljava/util/Set;" - && reference.name == "add" - } >= 0 && methodDef.containsLiteralInstruction(0) + instructions( + methodCall("this", "isInEditMode") + ) + custom { _, classDef -> + classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE } } internal val lottieCompositionFactoryZipFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - parameters("Landroid/content/Context;", "Ljava/lang/String;", "Ljava/lang/String;") + parameters("Landroid/content/Context;", "Ljava/util/zip/ZipInputStream;", "Ljava/lang/String;") returns("L") - strings(".zip", ".lottie") + instructions( + string("Unable to parse composition"), + string(" however it was not found in the animation.") + ) } /** @@ -154,7 +139,8 @@ internal val lottieCompositionFactoryFromJsonInputStreamFingerprint = fingerprin accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) parameters("Ljava/io/InputStream;", "Ljava/lang/String;") returns("L") - literal { 2 } + instructions( + anyInstruction(literal(2), literal(3)) + ) } - diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index f7332f5c3f..06a0b17174 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -4,27 +4,24 @@ import app.revanced.patcher.Fingerprint import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.fingerprint import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.shared.layout.theme.lithoColorHookPatch import app.revanced.patches.shared.layout.theme.lithoColorOverrideHook -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_49_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_30_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_34_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch -import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.insertLiteralOverride import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -33,65 +30,6 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter -internal var reelTimeBarPlayedColorId = -1L - private set -internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L - private set -internal var inlineTimeBarPlayedNotHighlightedColorId = -1L - private set -internal var ytYoutubeMagentaColorId = -1L - private set -internal var ytStaticBrandRedId = -1L - private set -internal var ytTextSecondaryId = -1L - private set -internal var inlineTimeBarLiveSeekableRangeId = -1L - private set - -private val seekbarColorResourcePatch = resourcePatch { - dependsOn( - settingsPatch, - resourceMappingPatch, - versionCheckPatch, - ) - - execute { - reelTimeBarPlayedColorId = resourceMappings[ - "color", - "reel_time_bar_played_color", - ] - inlineTimeBarColorizedBarPlayedColorDarkId = resourceMappings[ - "color", - "inline_time_bar_colorized_bar_played_color_dark", - ] - inlineTimeBarPlayedNotHighlightedColorId = resourceMappings[ - "color", - "inline_time_bar_played_not_highlighted_color", - ] - ytStaticBrandRedId = resourceMappings[ - "attr", - "ytStaticBrandRed" - ] - ytTextSecondaryId = resourceMappings[ - "attr", - "ytTextSecondary" - ] - inlineTimeBarLiveSeekableRangeId = resourceMappings[ - "color", - "inline_time_bar_live_seekable_range" - ] - - ytYoutubeMagentaColorId = resourceMappings[ - "color", - "yt_youtube_magenta", - ] - ytStaticBrandRedId = resourceMappings[ - "attr", - "ytStaticBrandRed", - ] - } -} - private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/theme/SeekbarColorPatch;" val seekbarColorPatch = bytecodePatch( @@ -100,29 +38,31 @@ val seekbarColorPatch = bytecodePatch( dependsOn( sharedExtensionPatch, lithoColorHookPatch, - seekbarColorResourcePatch, + resourceMappingPatch, versionCheckPatch ) execute { - fun MutableMethod.addColorChangeInstructions(resourceId: Long) { + fun MutableMethod.addColorChangeInstructions(index: Int) { insertLiteralOverride( - resourceId, + index, "$EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I" ) } - playerSeekbarColorFingerprint.method.apply { - addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDarkId) - addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColorId) + playerSeekbarColorFingerprint.let { + it.method.apply { + addColorChangeInstructions(it.instructionMatches.last().index) + addColorChangeInstructions(it.instructionMatches.first().index) + } } - shortsSeekbarColorFingerprint.method.apply { - addColorChangeInstructions(reelTimeBarPlayedColorId) + shortsSeekbarColorFingerprint.let { + it.method.addColorChangeInstructions(it.instructionMatches.first().index) } setSeekbarClickedColorFingerprint.originalMethod.let { - val setColorMethodIndex = setSeekbarClickedColorFingerprint.patternMatch!!.startIndex + 1 + val setColorMethodIndex = setSeekbarClickedColorFingerprint.instructionMatches.first().index + 1 navigate(it).to(setColorMethodIndex).stop().apply { val colorRegister = getInstruction(0).registerA @@ -131,7 +71,7 @@ val seekbarColorPatch = bytecodePatch( """ invoke-static { v$colorRegister }, $EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarClickedColor(I)I move-result v$colorRegister - """, + """ ) } } @@ -140,32 +80,31 @@ val seekbarColorPatch = bytecodePatch( // 19.25+ changes - arrayOf( - playerSeekbarHandle1ColorFingerprint, - playerSeekbarHandle2ColorFingerprint - ).forEach { - it.method.addColorChangeInstructions(ytStaticBrandRedId) + var handleBarColorFingerprints = mutableListOf(playerSeekbarHandle1ColorFingerprint) + if (!is_20_34_or_greater) { + handleBarColorFingerprints += playerSeekbarHandle2ColorFingerprint + } + handleBarColorFingerprints.forEach { + it.method.addColorChangeInstructions(it.instructionMatches.last().index) } // If hiding feed seekbar thumbnails, then turn off the cairo gradient // of the watch history menu items as they use the same gradient as the // player and there is no easy way to distinguish which to use a transparent color. if (is_19_34_or_greater) { - watchHistoryMenuUseProgressDrawableFingerprint.method.apply { - val progressIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.definingClass == "Landroid/widget/ProgressBar;" && reference.name == "setMax" - } - val index = indexOfFirstInstructionOrThrow(progressIndex, Opcode.MOVE_RESULT) - val register = getInstruction(index).registerA + watchHistoryMenuUseProgressDrawableFingerprint.let { + it.method.apply { + val index = it.instructionMatches[1].index + val register = getInstruction(index).registerA - addInstructions( - index + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->showWatchHistoryProgressDrawable(Z)Z - move-result v$register - """ - ) + addInstructions( + index + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->showWatchHistoryProgressDrawable(Z)Z + move-result v$register + """ + ) + } } } @@ -189,7 +128,7 @@ val seekbarColorPatch = bytecodePatch( playerFingerprint.let { it.method.apply { - val index = it.patternMatch!!.endIndex + val index = it.instructionMatches.last().index val register = getInstruction(index).registerA addInstructions( @@ -215,24 +154,14 @@ val seekbarColorPatch = bytecodePatch( return@execute // 19.25 does not have a cairo launch animation. } - // Add development hook to force old drawable splash animation. - arrayOf( - launchScreenLayoutTypeFingerprint, - mainActivityOnCreateFingerprint - ).forEach { fingerprint -> - fingerprint.method.insertLiteralOverride( - launchScreenLayoutTypeLotteFeatureFlag, - "$EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z" - ) - } - // Hook the splash animation to set the a seekbar color. mainActivityOnCreateFingerprint.method.apply { - val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name + val setAnimationIntMethodName = + lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name findInstructionIndicesReversedOrThrow { val reference = getReference() - reference?.definingClass == "Lcom/airbnb/lottie/LottieAnimationView;" + reference?.definingClass == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && reference.name == setAnimationIntMethodName }.forEach { index -> val instruction = getInstruction(index) @@ -240,7 +169,7 @@ val seekbarColorPatch = bytecodePatch( replaceInstruction( index, "invoke-static { v${instruction.registerC}, v${instruction.registerD} }, " + - "$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie(Lcom/airbnb/lottie/LottieAnimationView;I)V" + "$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie(Lcom/airbnb/lottie/LottieAnimationView;I)V" ) } } @@ -249,7 +178,8 @@ val seekbarColorPatch = bytecodePatch( // and `setAnimation(InputStream, String)` so extension code can call them. lottieAnimationViewSetAnimationIntFingerprint.classDef.methods.apply { val addedMethodName = "patch_setAnimation" - val setAnimationIntName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name + val setAnimationIntName = lottieAnimationViewSetAnimationIntFingerprint + .originalMethod.name add(ImmutableMethod( LOTTIE_ANIMATION_VIEW_CLASS_TYPE, @@ -269,9 +199,9 @@ val seekbarColorPatch = bytecodePatch( ) }) - val factoryStreamClass : CharSequence - val factoryStreamName : CharSequence - val factoryStreamReturnType : CharSequence + val factoryStreamClass: CharSequence + val factoryStreamName: CharSequence + val factoryStreamReturnType: CharSequence lottieCompositionFactoryFromJsonInputStreamFingerprint.match( lottieCompositionFactoryZipFingerprint.originalClassDef ).originalMethod.apply { @@ -280,6 +210,14 @@ val seekbarColorPatch = bytecodePatch( factoryStreamReturnType = returnType } + val lottieAnimationViewSetAnimationStreamFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters(factoryStreamReturnType.toString()) + returns("V") + custom { _, classDef -> + classDef.type == lottieAnimationViewSetAnimationIntFingerprint.originalClassDef.type + } + } val setAnimationStreamName = lottieAnimationViewSetAnimationStreamFingerprint .originalMethod.name diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt index 045d75ca28..0f809987e0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt @@ -1,51 +1,63 @@ package app.revanced.patches.youtube.layout.shortsautoplay +import app.revanced.patcher.InstructionLocation.* +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.reference.FieldReference -import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal val reelEnumConstructorFingerprint = fingerprint { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - opcodes(Opcode.RETURN_VOID) - strings( - "REEL_LOOP_BEHAVIOR_UNKNOWN", - "REEL_LOOP_BEHAVIOR_SINGLE_PLAY", - "REEL_LOOP_BEHAVIOR_REPEAT", - "REEL_LOOP_BEHAVIOR_END_SCREEN", + instructions( + string("REEL_LOOP_BEHAVIOR_UNKNOWN"), + string("REEL_LOOP_BEHAVIOR_SINGLE_PLAY"), + string("REEL_LOOP_BEHAVIOR_REPEAT"), + string("REEL_LOOP_BEHAVIOR_END_SCREEN"), + opcode(Opcode.RETURN_VOID) ) } +internal val reelPlaybackRepeatParentFingerprint = fingerprint { + returns("V") + parameters("Ljava/lang/String;", "J") + instructions( + string("Reels[%s] Playback Time: %d ms") + ) +} + +/** + * Matches class found in [reelPlaybackRepeatParentFingerprint]. + */ internal val reelPlaybackRepeatFingerprint = fingerprint { returns("V") parameters("L") - strings("YoutubePlayerState is in throwing an Error.") + instructions( + methodCall(smali = "Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z") + ) } internal val reelPlaybackFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") parameters("J") - custom { method, _ -> - indexOfMilliSecondsInstruction(method) >= 0 && - indexOfInitializationInstruction(method) >= 0 - } -} - -private fun indexOfMilliSecondsInstruction(method: Method) = - method.indexOfFirstInstruction { - getReference()?.name == "MILLISECONDS" - } - -internal fun indexOfInitializationInstruction(method: Method) = - method.indexOfFirstInstruction { - val reference = getReference() - opcode == Opcode.INVOKE_DIRECT && - reference?.name == "" && - reference.parameterTypes.size == 3 && - reference.parameterTypes.firstOrNull() == "I" - } + returns("V") + instructions( + fieldAccess( + definingClass = "Ljava/util/concurrent/TimeUnit;", + name = "MILLISECONDS" + ), + methodCall( + name = "", + parameters = listOf("I", "L", "L"), + location = MatchAfterWithin(15) + ), + methodCall( + opcode = Opcode.INVOKE_VIRTUAL, + parameters = listOf("L"), + returnType = "I", + location = MatchAfterWithin(5) + ) + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt index 30e9ecf8db..4613ad8a16 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt @@ -44,10 +44,10 @@ val shortsAutoplayPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -70,23 +70,25 @@ val shortsAutoplayPatch = bytecodePatch( "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->setMainActivity(Landroid/app/Activity;)V", ) - val reelEnumClass = reelEnumConstructorFingerprint.originalClassDef.type + var reelEnumClass : String - reelEnumConstructorFingerprint.method.apply { - val insertIndex = reelEnumConstructorFingerprint.patternMatch!!.startIndex + reelEnumConstructorFingerprint.let { + reelEnumClass = it.originalClassDef.type - addInstructions( - insertIndex, + it.method.addInstructions( + it.instructionMatches.last().index, """ # Pass the first enum value to extension. # Any enum value of this type will work. sget-object v0, $reelEnumClass->a:$reelEnumClass invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setYTShortsRepeatEnum(Ljava/lang/Enum;)V - """, + """ ) } - - reelPlaybackRepeatFingerprint.method.apply { + + reelPlaybackRepeatFingerprint.match( + reelPlaybackRepeatParentFingerprint.originalClassDef + ).method.apply { // The behavior enums are looked up from an ordinal value to an enum type. findInstructionIndicesReversedOrThrow { val reference = getReference() @@ -101,7 +103,7 @@ val shortsAutoplayPatch = bytecodePatch( """ invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->changeShortsRepeatBehavior(Ljava/lang/Enum;)Ljava/lang/Enum; move-result-object v$register - """, + """ ) } } @@ -110,13 +112,10 @@ val shortsAutoplayPatch = bytecodePatch( // Manually restore the removed 'Autoplay' code. if (is_20_09_or_greater) { // Variable names are only a rough guess of what these methods do. - val userActionMethodIndex = indexOfInitializationInstruction(reelPlaybackFingerprint.method) - val userActionMethodReference = reelPlaybackFingerprint.method - .getInstruction(userActionMethodIndex).reference as MethodReference - val reelSequenceControllerMethodIndex = reelPlaybackFingerprint.method - .indexOfFirstInstructionOrThrow(userActionMethodIndex, Opcode.INVOKE_VIRTUAL) - val reelSequenceControllerMethodReference = reelPlaybackFingerprint.method - .getInstruction(reelSequenceControllerMethodIndex).reference as MethodReference + val userActionMethodReference = reelPlaybackFingerprint.instructionMatches[1] + .getInstruction().reference as MethodReference + val reelSequenceControllerMethodReference = reelPlaybackFingerprint.instructionMatches[2] + .getInstruction().reference as MethodReference reelPlaybackRepeatFingerprint.method.apply { // Find the first call modified by extension code above. @@ -125,7 +124,7 @@ val shortsAutoplayPatch = bytecodePatch( getReference()?.definingClass == EXTENSION_CLASS_DESCRIPTOR } + 1 val enumRegister = getInstruction(extensionReturnResultIndex).registerA - val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow(extensionReturnResultIndex) { + val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow { val reference = getReference() opcode == Opcode.IGET_OBJECT && reference?.definingClass == definingClass && diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/Fingerprints.kt index abd7f10ceb..cf37e025f3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/Fingerprints.kt @@ -1,23 +1,61 @@ package app.revanced.patches.youtube.layout.shortsplayer +import app.revanced.patcher.checkCast +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode /** * Purpose of this method is not clear, and it's only used to identify * the obfuscated name of the videoId() method in PlaybackStartDescriptor. + * 20.38 and lower. */ internal val playbackStartFeatureFlagFingerprint = fingerprint { returns("Z") parameters( "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;", ) - literal { - 45380134L - } + instructions( + methodCall( + definingClass = "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;", + returnType = "Ljava/lang/String;" + ), + literal(45380134L) + ) +} + +/** + * Purpose of this method is not entirely clear, and it's only used to identify + * the obfuscated name of the videoId() method in PlaybackStartDescriptor. + * 20.39+ + */ +internal val watchPanelVideoIdFingerprint = fingerprint { + returns("Ljava/lang/String;") + parameters() + instructions( + fieldAccess( + opcode = Opcode.IGET_OBJECT, + type = "Lcom/google/android/apps/youtube/app/common/player/queue/WatchPanelId;" + ), + checkCast("Lcom/google/android/apps/youtube/app/common/player/queue/DefaultWatchPanelId;"), + methodCall( + definingClass = "Lcom/google/android/apps/youtube/app/common/player/queue/DefaultWatchPanelId;", + returnType = "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;" + ), + methodCall( + definingClass = "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;", + returnType = "Ljava/lang/String;" + ) + ) } + // Pre 19.25 internal val shortsPlaybackIntentLegacyFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) @@ -30,11 +68,12 @@ internal val shortsPlaybackIntentLegacyFingerprint = fingerprint { "Z", "Ljava/util/Map;" ) - strings( + instructions( + methodCall(returnType = "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"), // None of these strings are unique. - "com.google.android.apps.youtube.app.endpoint.flags", - "ReelWatchFragmentArgs", - "reels_fragment_descriptor" + string("com.google.android.apps.youtube.app.endpoint.flags"), + string("ReelWatchFragmentArgs"), + string("reels_fragment_descriptor") ) } @@ -47,18 +86,18 @@ internal val shortsPlaybackIntentFingerprint = fingerprint { "J", "Ljava/lang/String;" ) - strings( + instructions( // None of these strings are unique. - "com.google.android.apps.youtube.app.endpoint.flags", - "ReelWatchFragmentArgs", - "reels_fragment_descriptor" + string("com.google.android.apps.youtube.app.endpoint.flags"), + string("ReelWatchFragmentArgs"), + string("reels_fragment_descriptor") ) } internal val exitVideoPlayerFingerprint = fingerprint { returns("V") parameters() - literal { - mdx_drawer_layout_id - } + instructions( + resourceLiteral(ResourceType.ID, "mdx_drawer_layout") + ) } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatch.kt index 4637857542..65b32a8d96 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatch.kt @@ -5,18 +5,15 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.youtube.layout.player.fullscreen.openVideosFullscreenHookPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater -import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_39_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch @@ -32,21 +29,6 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch;" -internal var mdx_drawer_layout_id = -1L - private set - -private val openShortsInRegularPlayerResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - - execute { - mdx_drawer_layout_id = resourceMappings[ - "id", - "mdx_drawer_layout", - ] - - } -} - @Suppress("unused") val openShortsInRegularPlayerPatch = bytecodePatch( name = "Open Shorts in regular player", @@ -59,15 +41,15 @@ val openShortsInRegularPlayerPatch = bytecodePatch( openVideosFullscreenHookPatch, navigationBarHookPatch, versionCheckPatch, - openShortsInRegularPlayerResourcePatch + resourceMappingPatch ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -75,15 +57,7 @@ val openShortsInRegularPlayerPatch = bytecodePatch( addResources("youtube", "layout.shortsplayer.shortsPlayerTypePatch") PreferenceScreen.SHORTS.addPreferences( - if (is_19_46_or_greater) { - ListPreference("revanced_shorts_player_type") - } else { - ListPreference( - key = "revanced_shorts_player_type", - entriesKey = "revanced_shorts_player_type_legacy_entries", - entryValuesKey = "revanced_shorts_player_type_legacy_entry_values" - ) - } + ListPreference("revanced_shorts_player_type") ) // Activity is used as the context to launch an Intent. @@ -94,15 +68,16 @@ val openShortsInRegularPlayerPatch = bytecodePatch( ) // Find the obfuscated method name for PlaybackStartDescriptor.videoId() - val playbackStartVideoIdMethodName = playbackStartFeatureFlagFingerprint.method.let { - val stringMethodIndex = it.indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.definingClass == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;" - && reference.returnType == "Ljava/lang/String;" + val (videoIdStartMethod, videoIdIndex) = if (is_20_39_or_greater) { + watchPanelVideoIdFingerprint.let { + it.method to it.instructionMatches.last().index + } + } else { + playbackStartFeatureFlagFingerprint.let { + it.method to it.instructionMatches.first().index } - - navigate(it).to(stringMethodIndex).stop().name } + val playbackStartVideoIdMethodName = navigate(videoIdStartMethod).to(videoIdIndex).stop().name fun extensionInstructions(playbackStartRegister: Int, freeRegister: Int) = """ @@ -117,33 +92,30 @@ val openShortsInRegularPlayerPatch = bytecodePatch( nop """ - if (!is_19_25_or_greater) { - shortsPlaybackIntentLegacyFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow { - getReference()?.returnType == - "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;" + if (is_19_25_or_greater) { + shortsPlaybackIntentFingerprint.method.addInstructionsWithLabels( + 0, + """ + move-object/from16 v0, p1 + ${extensionInstructions(0, 1)} + """ + ) + } else { + shortsPlaybackIntentLegacyFingerprint.let { + it.method.apply { + val index = it.instructionMatches.first().index + val playbackStartRegister = getInstruction(index + 1).registerA + val insertIndex = index + 2 + val freeRegister = findFreeRegister(insertIndex, playbackStartRegister) + + addInstructionsWithLabels( + insertIndex, + extensionInstructions(playbackStartRegister, freeRegister) + ) } - val playbackStartRegister = getInstruction(index + 1).registerA - val insertIndex = index + 2 - val freeRegister = findFreeRegister(insertIndex, playbackStartRegister) - - addInstructionsWithLabels( - insertIndex, - extensionInstructions(playbackStartRegister, freeRegister) - ) } - - return@execute } - shortsPlaybackIntentFingerprint.method.addInstructionsWithLabels( - 0, - """ - move-object/from16 v0, p1 - ${extensionInstructions(0, 1)} - """ - ) - // Fix issue with back button exiting the app instead of minimizing the player. // Without this change this issue can be difficult to reproduce, but seems to occur // most often with 'open video in regular player' and not open in fullscreen player. diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt index dbbd0c0006..d7ea30c3d1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt @@ -1,63 +1,50 @@ package app.revanced.patches.youtube.layout.sponsorblock +import app.revanced.patcher.InstructionLocation.* +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral +import app.revanced.patches.youtube.shared.seekbarFingerprint import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionReversed import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal val appendTimeFingerprint = fingerprint { - returns("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") parameters("Ljava/lang/CharSequence;", "Ljava/lang/CharSequence;", "Ljava/lang/CharSequence;") - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IGET_OBJECT, - Opcode.IGET_OBJECT, - Opcode.CHECK_CAST, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT, + instructions( + resourceLiteral(ResourceType.STRING, "total_time"), + + methodCall(smali = "Landroid/content/res/Resources;->getString(I[Ljava/lang/Object;)Ljava/lang/String;"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterImmediately()) ) } internal val controlsOverlayFingerprint = fingerprint { returns("V") - accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) parameters() - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CHECK_CAST, // R.id.inset_overlay_view_layout - Opcode.IPUT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.CONST, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CHECK_CAST, - Opcode.NEW_INSTANCE, + instructions( + resourceLiteral(ResourceType.ID, "inset_overlay_view_layout"), + checkCast("Landroid/widget/FrameLayout;", MatchAfterWithin(20)) ) } +/** + * Resolves to the class found in [seekbarFingerprint]. + */ internal val rectangleFieldInvalidatorFingerprint = fingerprint { returns("V") - custom { method, _ -> - val instructions = method.implementation?.instructions!! - val instructionCount = instructions.count() - - // the method has definitely more than 5 instructions - if (instructionCount < 5) return@custom false - - val referenceInstruction = instructions.elementAt(instructionCount - 2) // the second to last instruction - val reference = ((referenceInstruction as? ReferenceInstruction)?.reference as? MethodReference) - - reference?.parameterTypes?.size == 1 && reference.name == "invalidate" // the reference is the invalidate(..) method - } + parameters() + instructions( + methodCall(name = "invalidate") + ) } internal val adProgressTextViewVisibilityFingerprint = fingerprint { @@ -76,3 +63,4 @@ internal fun indexOfAdProgressTextViewVisibilityInstruction(method: Method) = "Lcom/google/android/libraries/youtube/ads/player/ui/AdProgressTextView;" && reference.name =="setVisibility" } + diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt index 4b0835f8cc..3b38dac23b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt @@ -3,12 +3,8 @@ package app.revanced.patches.youtube.layout.sponsorblock import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch @@ -16,22 +12,31 @@ import app.revanced.patches.shared.misc.settings.preference.NonInteractivePrefer import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.playercontrols.* +import app.revanced.patches.youtube.misc.playercontrols.addTopControl +import app.revanced.patches.youtube.misc.playercontrols.initializeTopControl +import app.revanced.patches.youtube.misc.playercontrols.injectVisibilityCheckCall +import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.patches.youtube.shared.* +import app.revanced.patches.youtube.shared.layoutConstructorFingerprint +import app.revanced.patches.youtube.shared.seekbarFingerprint +import app.revanced.patches.youtube.shared.seekbarOnDrawFingerprint import app.revanced.patches.youtube.video.information.onCreateHook import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.patches.youtube.video.information.videoTimeHook import app.revanced.patches.youtube.video.videoid.hookBackgroundPlayVideoId import app.revanced.patches.youtube.video.videoid.videoIdPatch -import app.revanced.util.* -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.* +import app.revanced.util.ResourceGroup +import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.copyResources +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.StringReference private val sponsorBlockResourcePatch = resourcePatch { dependsOn( @@ -80,7 +85,6 @@ private val sponsorBlockResourcePatch = resourcePatch { "revanced_sb_skip_sponsor_button.xml", ), ResourceGroup( - // required resource for back button, because when the base APK is used, this resource will not exist "drawable", "revanced_sb_adjust.xml", "revanced_sb_backward.xml", @@ -88,6 +92,7 @@ private val sponsorBlockResourcePatch = resourcePatch { "revanced_sb_edit.xml", "revanced_sb_forward.xml", "revanced_sb_logo.xml", + "revanced_sb_logo_bold.xml", "revanced_sb_publish.xml", "revanced_sb_voting.xml", ) @@ -99,7 +104,7 @@ private val sponsorBlockResourcePatch = resourcePatch { } } -private const val EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR = +internal const val EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/sponsorblock/SegmentPlaybackController;" private const val EXTENSION_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton;" @@ -115,6 +120,7 @@ val sponsorBlockPatch = bytecodePatch( ) { dependsOn( sharedExtensionPatch, + resourceMappingPatch, videoIdPatch, // Required to skip segments on time. videoInformationPatch, @@ -126,10 +132,10 @@ val sponsorBlockPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -145,42 +151,63 @@ val sponsorBlockPatch = bytecodePatch( "->setCurrentVideoId(Ljava/lang/String;)V", ) - // Seekbar drawing - seekbarOnDrawFingerprint.match(seekbarFingerprint.originalClassDef).method.apply { - // Get left and right of seekbar rectangle. - val moveRectangleToRegisterIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_OBJECT_FROM16) + // Set seekbar draw rectangle. + val rectangleFieldName: FieldReference + rectangleFieldInvalidatorFingerprint.match( + seekbarFingerprint.originalClassDef + ).let { + it.method.apply { + val rectangleIndex = indexOfFirstInstructionReversedOrThrow( + it.instructionMatches.first().index + ) { + getReference()?.type == "Landroid/graphics/Rect;" + } + rectangleFieldName = getInstruction(rectangleIndex).reference as FieldReference + } + } - addInstruction( - moveRectangleToRegisterIndex + 1, - "invoke-static/range { p0 .. p0 }, " + - "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;)V", - ) + // Seekbar drawing. - // Set the thickness of the segment. - val thicknessIndex = indexOfFirstInstructionOrThrow { - opcode == Opcode.INVOKE_STATIC && getReference()?.name == "round" - } - val thicknessRegister = getInstruction(thicknessIndex).registerC - addInstruction( - thicknessIndex + 2, - "invoke-static { v$thicknessRegister }, " + - "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarThickness(I)V", - ) + // Shared fingerprint and indexes may have changed. + seekbarOnDrawFingerprint.clearMatch() + // Cannot match using original immutable class because + // class may have been modified by other patches + seekbarOnDrawFingerprint.match(seekbarFingerprint.classDef).let { + it.method.apply { + // Set seekbar thickness. + val thicknessIndex = it.instructionMatches.last().index + val thicknessRegister = getInstruction(thicknessIndex).registerA + addInstruction( + thicknessIndex + 1, + "invoke-static { v$thicknessRegister }, " + + "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSeekbarThickness(I)V", + ) - // Find the drawCircle call and draw the segment before it. - val drawCircleIndex = indexOfFirstInstructionReversedOrThrow { - getReference()?.name == "drawCircle" - } - val drawCircleInstruction = getInstruction(drawCircleIndex) - val canvasInstanceRegister = drawCircleInstruction.registerC - val centerYRegister = drawCircleInstruction.registerE + // Find the drawCircle call and draw the segment before it. + val drawCircleIndex = indexOfFirstInstructionReversedOrThrow { + getReference()?.name == "drawCircle" + } + val drawCircleInstruction = getInstruction(drawCircleIndex) + val canvasInstanceRegister = drawCircleInstruction.registerC + val centerYRegister = drawCircleInstruction.registerE - addInstruction( - drawCircleIndex, - "invoke-static { v$canvasInstanceRegister, v$centerYRegister }, " + - "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->" + - "drawSponsorTimeBars(Landroid/graphics/Canvas;F)V", - ) + addInstruction( + drawCircleIndex, + "invoke-static { v$canvasInstanceRegister, v$centerYRegister }, " + + "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->" + + "drawSegmentTimeBars(Landroid/graphics/Canvas;F)V", + ) + + // Set seekbar bounds. + addInstructions( + 0, + """ + move-object/from16 v0, p0 + iget-object v0, v0, $rectangleFieldName + invoke-static { v0 }, $EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSeekbarRectangle(Landroid/graphics/Rect;)V + """ + ) + } } // Change visibility of the buttons. @@ -191,17 +218,19 @@ val sponsorBlockPatch = bytecodePatch( injectVisibilityCheckCall(EXTENSION_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR) // Append the new time to the player layout. - val appendTimePatternScanStartIndex = appendTimeFingerprint.patternMatch!!.startIndex - appendTimeFingerprint.method.apply { - val register = getInstruction(appendTimePatternScanStartIndex + 1).registerA + appendTimeFingerprint.let { + it.method.apply { + val index = it.instructionMatches.last().index + val register = getInstruction(index).registerA - addInstructions( - appendTimePatternScanStartIndex + 2, - """ - invoke-static { v$register }, $EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->appendTimeWithoutSegments(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$register - """ - ) + addInstructions( + index + 1, + """ + invoke-static { v$register }, $EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->appendTimeWithoutSegments(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$register + """ + ) + } } // Initialize the player controller. @@ -209,48 +238,16 @@ val sponsorBlockPatch = bytecodePatch( // Initialize the SponsorBlock view. controlsOverlayFingerprint.match(layoutConstructorFingerprint.originalClassDef).let { - val startIndex = it.patternMatch!!.startIndex + val checkCastIndex = it.instructionMatches.last().index it.method.apply { - val frameLayoutRegister = (getInstruction(startIndex + 2) as OneRegisterInstruction).registerA + val frameLayoutRegister = getInstruction(checkCastIndex).registerA addInstruction( - startIndex + 3, + checkCastIndex + 1, "invoke-static {v$frameLayoutRegister}, $EXTENSION_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/ViewGroup;)V", ) } } - // Set seekbar draw rectangle. - rectangleFieldInvalidatorFingerprint.match(seekbarOnDrawFingerprint.originalClassDef).method.apply { - val fieldIndex = instructions.count() - 3 - val fieldReference = getInstruction(fieldIndex).reference as FieldReference - - // replace the "replaceMeWith*" strings - proxy(classes.first { it.type.endsWith("SegmentPlaybackController;") }) - .mutableClass - .methods - .find { it.name == "setSponsorBarRect" } - ?.let { method -> - fun MutableMethod.replaceStringInstruction(index: Int, instruction: Instruction, with: String) { - val register = (instruction as OneRegisterInstruction).registerA - this.replaceInstruction( - index, - "const-string v$register, \"$with\"", - ) - } - for ((index, it) in method.instructions.withIndex()) { - if (it.opcode.ordinal != Opcode.CONST_STRING.ordinal) continue - - when (((it as ReferenceInstruction).reference as StringReference).string) { - "replaceMeWithsetSponsorBarRect" -> method.replaceStringInstruction( - index, - it, - fieldReference.name, - ) - } - } - } ?: throw PatchException("Could not find the method which contains the replaceMeWith* strings") - } - adProgressTextViewVisibilityFingerprint.method.apply { val index = indexOfAdProgressTextViewVisibilityInstruction(this) val register = getInstruction(index).registerD @@ -260,6 +257,5 @@ val sponsorBlockPatch = bytecodePatch( "invoke-static { v$register }, $EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setAdProgressTextVisibility(I)V" ) } - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt index ac458cec26..c740718937 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt @@ -1,30 +1,34 @@ package app.revanced.patches.youtube.layout.spoofappversion +import app.revanced.patcher.InstructionLocation.* +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal val toolBarButtonFingerprint = fingerprint { - returns("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Landroid/view/MenuItem;") + returns("V") + instructions( + resourceLiteral(ResourceType.ID, "menu_item_view"), + methodCall(returnType = "I", opcode = Opcode.INVOKE_INTERFACE), + opcode(Opcode.MOVE_RESULT, MatchAfterImmediately()), + fieldAccess(type = "Landroid/widget/ImageView;", opcode = Opcode.IGET_OBJECT, location = MatchAfterWithin(6)), + methodCall("Landroid/content/res/Resources;", "getDrawable", location = MatchAfterWithin(8)), + methodCall("Landroid/widget/ImageView;", "setImageDrawable", location = MatchAfterWithin(4)) + ) custom { method, _ -> - method.containsLiteralInstruction(menuItemView) && - indexOfGetDrawableInstruction(method) >= 0 + // 20.37+ has second parameter of "Landroid/content/Context;" + val parameterCount = method.parameterTypes.count() + (parameterCount == 1 || parameterCount == 2) + && method.parameterTypes.firstOrNull() == "Landroid/view/MenuItem;" } } -internal fun indexOfGetDrawableInstruction(method: Method) = method.indexOfFirstInstruction { - val reference = getReference() - reference?.definingClass == "Landroid/content/res/Resources;" && - reference.name == "getDrawable" -} - internal val spoofAppVersionFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("L") diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt index 5bc0e2ba2a..11facb0c21 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt @@ -4,13 +4,10 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting @@ -21,25 +18,7 @@ import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow -import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference - -internal var menuItemView = -1L - private set - -internal val spoofAppVersionResourcePatch = resourcePatch { - dependsOn( - resourceMappingPatch - ) - - execute { - menuItemView = resourceMappings["id", "menu_item_view"] - } -} private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch;" @@ -50,7 +29,7 @@ val spoofAppVersionPatch = bytecodePatch( "This can be used to restore old UI elements and features." ) { dependsOn( - spoofAppVersionResourcePatch, + resourceMappingPatch, sharedExtensionPatch, settingsPatch, addResourcesPatch, @@ -59,10 +38,10 @@ val spoofAppVersionPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -83,14 +62,12 @@ val spoofAppVersionPatch = bytecodePatch( } else if (is_19_43_or_greater) { ListPreference( key = "revanced_spoof_app_version_target", - summaryKey = null, entriesKey = "revanced_spoof_app_version_target_legacy_20_13_entries", entryValuesKey = "revanced_spoof_app_version_target_legacy_20_13_entry_values" ) } else { ListPreference( key = "revanced_spoof_app_version_target", - summaryKey = null, entriesKey = "revanced_spoof_app_version_target_legacy_19_34_entries", entryValuesKey = "revanced_spoof_app_version_target_legacy_19_34_entry_values" ) @@ -104,35 +81,27 @@ val spoofAppVersionPatch = bytecodePatch( * missing image resources. As a workaround, do not set an image in the * toolbar when the enum name is UNKNOWN. */ - toolBarButtonFingerprint.method.apply { - val getDrawableIndex = indexOfGetDrawableInstruction(this) - val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) { - opcode == Opcode.INVOKE_INTERFACE && - getReference()?.returnType == "I" - } - val insertIndex = enumOrdinalIndex + 2 - val insertRegister = getInstruction(insertIndex - 1).registerA - val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex) { - opcode == Opcode.INVOKE_VIRTUAL && - getReference()?.name == "setImageDrawable" - } + 1 + toolBarButtonFingerprint.apply { + val imageResourceIndex = instructionMatches[2].index + val register = method.getInstruction(imageResourceIndex).registerA + val jumpIndex = instructionMatches.last().index + 1 - addInstructionsWithLabels( - insertIndex, - "if-eqz v$insertRegister, :ignore", - ExternalLabel("ignore", getInstruction(jumpIndex)) + method.addInstructionsWithLabels( + imageResourceIndex + 1, + "if-eqz v$register, :ignore", + ExternalLabel("ignore", method.getInstruction(jumpIndex)) ) } spoofAppVersionFingerprint.apply { - val startIndex = patternMatch!!.startIndex - val buildOverrideNameRegister = method.getInstruction(startIndex).registerA + val index = instructionMatches.first().index + val register = method.getInstruction(index).registerA method.addInstructions( - startIndex + 1, + index + 1, """ - invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$buildOverrideNameRegister + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$register """ ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt index a56fa467d5..1a10ceebcb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt @@ -13,10 +13,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.StringReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeStartPagePatch;" @@ -32,10 +29,10 @@ val changeStartPagePatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -58,19 +55,19 @@ val changeStartPagePatch = bytecodePatch( ) // Hook browseId. - browseIdFingerprint.method.apply { - val browseIdIndex = indexOfFirstInstructionOrThrow { - getReference()?.string == "FEwhat_to_watch" - } - val browseIdRegister = getInstruction(browseIdIndex).registerA + browseIdFingerprint.let { + it.method.apply { + val browseIdIndex = it.instructionMatches.first().index + val browseIdRegister = getInstruction(browseIdIndex).registerA - addInstructions( - browseIdIndex + 1, - """ - invoke-static { v$browseIdRegister }, $EXTENSION_CLASS_DESCRIPTOR->overrideBrowseId(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$browseIdRegister - """, - ) + addInstructions( + browseIdIndex + 1, + """ + invoke-static { v$browseIdRegister }, $EXTENSION_CLASS_DESCRIPTOR->overrideBrowseId(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$browseIdRegister + """ + ) + } } // There is no browserId assigned to Shorts and Search. diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt index 0220840203..ebc4ad2715 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt @@ -1,20 +1,25 @@ package app.revanced.patches.youtube.layout.startpage +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint +import app.revanced.patcher.literal +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.Opcode internal val intentActionFingerprint = fingerprint { parameters("Landroid/content/Intent;") - strings("has_handled_intent") + instructions( + string("has_handled_intent") + ) } internal val browseIdFingerprint = fingerprint { returns("Lcom/google/android/apps/youtube/app/common/ui/navigation/PaneDescriptor;") - parameters() - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.RETURN_OBJECT, + + //parameters() // 20.30 and earlier is no parameters. 20.31+ parameter is L. + instructions( + string("FEwhat_to_watch"), + literal(512), + fieldAccess(opcode = Opcode.IPUT_OBJECT, type = "Ljava/lang/String;") ) - strings("FEwhat_to_watch") } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt index 10ef950970..aec499c26b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt @@ -1,20 +1,19 @@ package app.revanced.patches.youtube.layout.startupshortsreset import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.playservice.is_20_02_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_03_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findFreeRegister import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference @@ -30,14 +29,15 @@ val disableResumingShortsOnStartupPatch = bytecodePatch( sharedExtensionPatch, settingsPatch, addResourcesPatch, + versionCheckPatch ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -48,45 +48,41 @@ val disableResumingShortsOnStartupPatch = bytecodePatch( SwitchPreference("revanced_disable_resuming_shorts_player"), ) - if (is_20_02_or_greater) { + if (is_20_03_or_greater) { userWasInShortsAlternativeFingerprint.let { it.method.apply { - val stringIndex = it.stringMatches!!.first().index - val booleanValueIndex = indexOfFirstInstructionReversedOrThrow(stringIndex) { - opcode == Opcode.INVOKE_VIRTUAL && - getReference()?.name == "booleanValue" - } - val booleanValueRegister = - getInstruction(booleanValueIndex + 1).registerA + val match = it.instructionMatches[2] + val insertIndex = match.index + 1 + val register = match.getInstruction().registerA addInstructions( - booleanValueIndex + 2, """ - invoke-static {v$booleanValueRegister}, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer(Z)Z - move-result v$booleanValueRegister - """ + insertIndex, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer(Z)Z + move-result v$register + """ ) } } } else { userWasInShortsLegacyFingerprint.method.apply { val listenableInstructionIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() opcode == Opcode.INVOKE_INTERFACE && - reference?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" && - reference.name == "isDone" + getReference()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" && + getReference()?.name == "isDone" } val freeRegister = findFreeRegister(listenableInstructionIndex) addInstructionsAtControlFlowLabel( listenableInstructionIndex, """ - invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z move-result v$freeRegister - if-eqz v$freeRegister, :show + if-eqz v$freeRegister, :show_startup_shorts_player return-void - :show + :show_startup_shorts_player nop - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt index b1fab6fcb9..586396db9f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt @@ -1,24 +1,42 @@ package app.revanced.patches.youtube.layout.startupshortsreset +import app.revanced.patcher.InstructionLocation.* +import app.revanced.patcher.StringComparisonType +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode /** - * YouTube 20.02.08 ~ + * 20.02+ */ internal val userWasInShortsAlternativeFingerprint = fingerprint { returns("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters("Ljava/lang/Object;") - strings("userIsInShorts: ") + instructions( + checkCast("Ljava/lang/Boolean;"), + methodCall(smali = "Ljava/lang/Boolean;->booleanValue()Z", location = MatchAfterImmediately()), + opcode(Opcode.MOVE_RESULT, MatchAfterImmediately()), + // 20.40+ string was merged into another string and is a partial match. + string("userIsInShorts: ", StringComparisonType.CONTAINS, MatchAfterWithin(15)) + ) } +/** + * Pre 20.02 + */ internal val userWasInShortsLegacyFingerprint = fingerprint { returns("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) parameters("Ljava/lang/Object;") - strings("Failed to read user_was_in_shorts proto after successful warmup") + instructions( + string("Failed to read user_was_in_shorts proto after successful warmup") + ) } /** @@ -27,7 +45,8 @@ internal val userWasInShortsLegacyFingerprint = fingerprint { internal val userWasInShortsConfigFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") - literal { - 45358360L - } + parameters() + instructions( + literal(45358360L) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt deleted file mode 100644 index c04f5e99c7..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.youtube.layout.tablet - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.layout.formfactor.changeFormFactorPatch - -@Deprecated("Use 'Change form factor' instead.") -val enableTabletLayoutPatch = bytecodePatch { - dependsOn(changeFormFactorPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt index 69df831091..eca98e822f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt @@ -1,21 +1,25 @@ package app.revanced.patches.youtube.layout.theme +import app.revanced.patcher.anyInstruction import app.revanced.patcher.fingerprint +import app.revanced.patcher.literal import app.revanced.patches.youtube.shared.YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE -import app.revanced.util.literal - -internal const val GRADIENT_LOADING_SCREEN_AB_CONSTANT = 45412406L internal val useGradientLoadingScreenFingerprint = fingerprint { - literal { GRADIENT_LOADING_SCREEN_AB_CONSTANT } + instructions( + literal(45412406L) + ) } -internal const val SPLASH_SCREEN_STYLE_FEATURE_FLAG = 269032877L - internal val splashScreenStyleFingerprint = fingerprint { returns("V") parameters("Landroid/os/Bundle;") - literal { SPLASH_SCREEN_STYLE_FEATURE_FLAG } + instructions( + anyInstruction( + literal(1074339245), // 20.30+ + literal(269032877L) // 20.29 and lower. + ) + ) custom { method, classDef -> method.name == "onCreate" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt deleted file mode 100644 index a0bd5e7165..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt +++ /dev/null @@ -1,19 +0,0 @@ -package app.revanced.patches.youtube.layout.theme - -import app.revanced.patcher.patch.bytecodePatch - - -@Deprecated("Function was moved", ReplaceWith("app.revanced.patches.shared.layout.theme.lithoColorOverrideHook")) -@Suppress("unused") -lateinit var lithoColorOverrideHook: (targetMethodClass: String, targetMethodName: String) -> Unit - private set - -@Deprecated("Patch was moved", ReplaceWith("app.revanced.patches.shared.layout.theme.lithoColorHookPatch")) -@Suppress("unused") -val lithoColorHookPatch = bytecodePatch{ - dependsOn(app.revanced.patches.shared.layout.theme.lithoColorHookPatch) - - execute { - lithoColorOverrideHook = app.revanced.patches.shared.layout.theme.lithoColorOverrideHook - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt index e6022b0597..15782751d3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt @@ -115,7 +115,7 @@ val themePatch = baseThemePatch( // Fix the splash screen dark mode background color. // In 19.32+ the dark mode splash screen is white and fades to black. - document("res/values-night-v27/styles.xml").use { document -> + document("res/values-night/styles.xml").use { document -> // Create a night mode specific override for the splash screen background. val style = document.createElement("style") style.setAttribute("name", "Theme.YouTube.Home") @@ -156,10 +156,10 @@ val themePatch = baseThemePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) }, @@ -200,17 +200,21 @@ val themePatch = baseThemePatch( ) } - useGradientLoadingScreenFingerprint.method.insertLiteralOverride( - GRADIENT_LOADING_SCREEN_AB_CONSTANT, - "$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z" - ) + useGradientLoadingScreenFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z" + ) + } if (is_19_47_or_greater) { // Lottie splash screen exists in earlier versions, but it may not be always on. - splashScreenStyleFingerprint.method.insertLiteralOverride( - SPLASH_SCREEN_STYLE_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->getLoadingScreenType(I)I" - ) + splashScreenStyleFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$EXTENSION_CLASS_DESCRIPTOR->getLoadingScreenType(I)I" + ) + } } } ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt index b96330de84..d98ca06f67 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt @@ -33,10 +33,10 @@ val alternativeThumbnailsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt index 2e90053831..ac90f1db21 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt @@ -27,10 +27,10 @@ val bypassImageRegionRestrictionsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt index 25cc81d494..15ce07f7f5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt @@ -23,10 +23,10 @@ val announcementsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt deleted file mode 100644 index 655f6d1762..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.youtube.misc.autorepeat - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.misc.loopvideo.loopVideoPatch - -@Deprecated("Patch was renamed", ReplaceWith("looVideoPatch")) -val autoRepeatPatch = bytecodePatch { - dependsOn(loopVideoPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt index ae8fe497a8..c227da0561 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -3,12 +3,11 @@ package app.revanced.patches.youtube.misc.backgroundplayback import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch @@ -25,14 +24,6 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal var prefBackgroundAndOfflineCategoryId = -1L private set -private val backgroundPlaybackResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch, addResourcesPatch) - - execute { - prefBackgroundAndOfflineCategoryId = resourceMappings["string", "pref_background_and_offline_category"] - } -} - private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/BackgroundPlaybackPatch;" @@ -41,7 +32,8 @@ val backgroundPlaybackPatch = bytecodePatch( description = "Removes restrictions on background playback, including playing kids videos in the background.", ) { dependsOn( - backgroundPlaybackResourcePatch, + resourceMappingPatch, + addResourcesPatch, sharedExtensionPatch, playerTypeHookPatch, videoInformationPatch, @@ -51,10 +43,10 @@ val backgroundPlaybackPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -62,7 +54,12 @@ val backgroundPlaybackPatch = bytecodePatch( addResources("youtube", "misc.backgroundplayback.backgroundPlaybackPatch") PreferenceScreen.SHORTS.addPreferences( - SwitchPreference("revanced_shorts_disable_background_playback"), + SwitchPreference("revanced_shorts_disable_background_playback") + ) + + prefBackgroundAndOfflineCategoryId = getResourceId( + ResourceType.STRING, + "pref_background_and_offline_category" ) arrayOf( @@ -104,10 +101,12 @@ val backgroundPlaybackPatch = bytecodePatch( // Fix PiP buttons not working after locking/unlocking device screen. if (is_19_34_or_greater) { - pipInputConsumerFeatureFlagFingerprint.method.insertLiteralOverride( - PIP_INPUT_CONSUMER_FEATURE_FLAG, - false - ) + pipInputConsumerFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + false + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt index a5c077115d..cccc893567 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt @@ -75,19 +75,24 @@ internal val backgroundPlaybackManagerShortsFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("Z") parameters("L") - literal { 151635310 } + instructions( + app.revanced.patcher.literal(151635310) + ) } internal val shortsBackgroundPlaybackFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") parameters() - literal { 45415425 } + instructions( + app.revanced.patcher.literal(45415425) + ) } -internal const val PIP_INPUT_CONSUMER_FEATURE_FLAG = 45638483L - // Fix 'E/InputDispatcher: Window handle pip_input_consumer has no registered input channel' internal val pipInputConsumerFeatureFlagFingerprint = fingerprint { - literal { PIP_INPUT_CONSUMER_FEATURE_FLAG} + instructions( + // PiP input consumer feature flag. + app.revanced.patcher.literal(45638483L) + ) } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt index a8f55f46c2..a85cb983fb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt @@ -17,10 +17,10 @@ val enableDebuggingPatch = enableDebuggingPatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) }, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/Fingerprints.kt deleted file mode 100644 index 33e188974b..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/Fingerprints.kt +++ /dev/null @@ -1,35 +0,0 @@ -package app.revanced.patches.youtube.misc.debugging - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags - -internal val experimentalFeatureFlagParentFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("L") - parameters("L", "J", "[B") - strings("Unable to parse proto typed experiment flag: ") -} - -internal val experimentalBooleanFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("L", "J", "Z") -} - -internal val experimentalDoubleFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("D") - parameters("J", "D") -} - -internal val experimentalLongFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("J") - parameters("J", "J") -} - -internal val experimentalStringFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters("J", "Ljava/lang/String;") -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt index 4f99a4cf54..71fb0f9d1b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt @@ -1,8 +1,12 @@ package app.revanced.patches.youtube.misc.dimensions.spoof import app.revanced.patcher.fingerprint +import app.revanced.patcher.string internal val deviceDimensionsModelToStringFingerprint = fingerprint { returns("L") - strings("minh.", ";maxh.") + instructions( + string("minh."), + string(";maxh.") + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt index c43d301d79..6dc075f35e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt @@ -24,10 +24,10 @@ val spoofDeviceDimensionsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -43,7 +43,7 @@ val spoofDeviceDimensionsPatch = bytecodePatch( // Override the parameters containing the dimensions. .addInstructions( 1, // Add after super call. - mapOf( + arrayOf( 1 to "MinHeightOrWidth", // p1 = min height 2 to "MaxHeightOrWidth", // p2 = max height 3 to "MinHeightOrWidth", // p3 = min width diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt index 5bd5da4151..b0898cdc5f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt @@ -12,10 +12,10 @@ val checkWatchHistoryDomainNameResolutionPatch = checkWatchHistoryDomainNameReso compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) }, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt index 743d1162db..82c80931ad 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt @@ -1,5 +1,7 @@ package app.revanced.patches.youtube.misc.extension.hooks +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook import app.revanced.patches.shared.misc.extension.extensionHook import app.revanced.patches.youtube.shared.YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE @@ -10,14 +12,12 @@ import app.revanced.patches.youtube.shared.YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE internal val applicationInitHook = extensionHook { // Does _not_ resolve to the YouTube main activity. // Required as some hooked code runs before the main activity is launched. - strings("Application creation", "Application.onCreate") -} - -internal val applicationInitOnCrateHook = extensionHook { - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - method.name == "onCreate" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE - } + instructions( + string("Application.onCreate"), + string("Application creation") + ) } +internal val applicationInitOnCrateHook = activityOnCreateExtensionHook( + YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt index 7877ce7dbf..a084703e4c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt @@ -1,6 +1,11 @@ package app.revanced.patches.youtube.misc.fix.backtoexitgesture +import app.revanced.patcher.InstructionLocation.* +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -16,35 +21,16 @@ internal val scrollPositionFingerprint = fingerprint { strings("scroll_position") } -/** - * Resolves using class found in [recyclerViewTopScrollingParentFingerprint]. - */ internal val recyclerViewTopScrollingFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters() - opcodes( - Opcode.CHECK_CAST, - Opcode.CONST_4, - Opcode.INVOKE_VIRTUAL, - Opcode.GOTO, - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE - ) -} - -internal val recyclerViewTopScrollingParentFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - opcodes( - Opcode.IPUT_OBJECT, - Opcode.IPUT_OBJECT, - Opcode.IPUT_OBJECT, - Opcode.IPUT_OBJECT, - Opcode.CONST_16, - Opcode.INVOKE_VIRTUAL, - Opcode.NEW_INSTANCE, - Opcode.INVOKE_DIRECT, - Opcode.IPUT_OBJECT, - Opcode.RETURN_VOID + instructions( + methodCall(smali = "Ljava/util/Iterator;->next()Ljava/lang/Object;"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterImmediately()), + checkCast("Landroid/support/v7/widget/RecyclerView;", MatchAfterImmediately()), + literal(0, location = MatchAfterImmediately()), + methodCall(definingClass = "Landroid/support/v7/widget/RecyclerView;", location = MatchAfterImmediately()), + opcode(Opcode.GOTO, MatchAfterImmediately()) ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt index cf55ed6b56..af3ca8ca99 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt @@ -3,6 +3,7 @@ package app.revanced.patches.youtube.misc.fix.backtoexitgesture import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.youtube.shared.mainActivityOnBackPressedFingerprint +import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode @@ -15,11 +16,9 @@ internal val fixBackToExitGesturePatch = bytecodePatch( ) { execute { - recyclerViewTopScrollingFingerprint.match( - recyclerViewTopScrollingParentFingerprint.originalClassDef - ).let { - it.method.addInstruction( - it.patternMatch!!.endIndex, + recyclerViewTopScrollingFingerprint.let { + it.method.addInstructionsAtControlFlowLabel( + it.instructionMatches.last().index + 1, "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onTopView()V" ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/contentprovider/FIxContentProviderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/contentprovider/FIxContentProviderPatch.kt new file mode 100644 index 0000000000..de4e9a2b65 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/contentprovider/FIxContentProviderPatch.kt @@ -0,0 +1,35 @@ +package app.revanced.patches.youtube.misc.fix.contentprovider + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/FixContentProviderPatch;" + +/** + * Fixes crashing for some users with a beta release where the YouTube content provider uses null map values. + * It unknown if this crash can happen on stable releases. + */ +internal val fixContentProviderPatch = bytecodePatch{ + dependsOn( + sharedExtensionPatch + ) + + execute { + unstableContentProviderFingerprint.let { + val insertIndex = it.instructionMatches.first().index + + it.method.apply { + val register = getInstruction(insertIndex).registerD + + it.method.addInstruction( + insertIndex, + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->removeNullMapEntries(Ljava/util/Map;)V" + ) + } + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/contentprovider/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/contentprovider/Fingerprints.kt new file mode 100644 index 0000000000..7e3ab83c2c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/contentprovider/Fingerprints.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.youtube.misc.fix.contentprovider + +import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall +import app.revanced.patcher.string +import com.android.tools.smali.dexlib2.AccessFlags + +internal val unstableContentProviderFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Landroid/content/ContentResolver;", "[Ljava/lang/String;") + instructions( + // Early targets use HashMap and later targets use ConcurrentMap. + methodCall( + name = "putAll", + parameters = listOf("Ljava/util/Map;") + ), + string("ContentProvider query returned null cursor") + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt deleted file mode 100644 index 89e7cc08f8..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.youtube.misc.fix.playback - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Use app.revanced.patches.youtube.misc.spoof.spoofVideoStreamsPatch instead.") -@Suppress("unused") -val spoofVideoStreamsPatch = bytecodePatch { - dependsOn(app.revanced.patches.youtube.misc.spoof.spoofVideoStreamsPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt deleted file mode 100644 index eb4c9492be..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.youtube.misc.fix.playback - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Use app.revanced.patches.youtube.misc.spoof.userAgentClientSpoofPatch instead.") -@Suppress("unused") -val userAgentClientSpoofPatch = bytecodePatch { - dependsOn(app.revanced.patches.youtube.misc.spoof.userAgentClientSpoofPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt index b63eec7941..f97139e40f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt @@ -29,7 +29,6 @@ private const val EXTENSION_CLASS_DESCRIPTOR = * 6. Resume the video * 7. Playback speed will incorrectly change to 1.0x. */ -@Suppress("unused") val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{ dependsOn( sharedExtensionPatch, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/AccountCredentialsInvalidTextPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/AccountCredentialsInvalidTextPatch.kt index 673fa240a8..93e912addc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/AccountCredentialsInvalidTextPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/AccountCredentialsInvalidTextPatch.kt @@ -3,44 +3,17 @@ package app.revanced.patches.youtube.misc.gms import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/AccountCredentialsInvalidTextPatch;" -internal var ic_offline_no_content_upside_down = -1L - private set -internal var offline_no_content_body_text_not_offline_eligible = -1L - private set - -private val accountCredentialsInvalidTextResourcePatch = resourcePatch { - execute { - ic_offline_no_content_upside_down = resourceMappings[ - "drawable", - "ic_offline_no_content_upside_down" - ] - - offline_no_content_body_text_not_offline_eligible = resourceMappings[ - "string", - "offline_no_content_body_text_not_offline_eligible" - ] - } -} - internal val accountCredentialsInvalidTextPatch = bytecodePatch { dependsOn( sharedExtensionPatch, - accountCredentialsInvalidTextResourcePatch, addResourcesPatch ) @@ -60,18 +33,12 @@ internal val accountCredentialsInvalidTextPatch = bytecodePatch { specificNetworkErrorViewControllerFingerprint, loadingFrameLayoutControllerFingerprint ).forEach { fingerprint -> - fingerprint.method.apply { - val resourceIndex = indexOfFirstLiteralInstructionOrThrow( - offline_no_content_body_text_not_offline_eligible - ) - val getStringIndex = indexOfFirstInstructionOrThrow(resourceIndex) { - val reference = getReference() - reference?.name == "getString" - } - val register = getInstruction(getStringIndex + 1).registerA + fingerprint.apply { + val index = instructionMatches.last().index + val register = method.getInstruction(index).registerA - addInstructions( - getStringIndex + 2, + method.addInstructions( + index + 1, """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getOfflineNetworkErrorString(Ljava/lang/String;)Ljava/lang/String; move-result-object v$register diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/Fingerprints.kt index 3f4a521df6..0353e38417 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/Fingerprints.kt @@ -1,17 +1,24 @@ package app.revanced.patches.youtube.misc.gms +import app.revanced.patcher.InstructionLocation.* import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode internal val specificNetworkErrorViewControllerFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters() - custom { method, _ -> - method.containsLiteralInstruction(ic_offline_no_content_upside_down) - && method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible) - } + instructions( + resourceLiteral(ResourceType.DRAWABLE, "ic_offline_no_content_upside_down"), + resourceLiteral(ResourceType.STRING, "offline_no_content_body_text_not_offline_eligible"), + methodCall(name = "getString", returnType = "Ljava/lang/String;"), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterImmediately()) + ) } // It's not clear if this second class is ever used and it may be dead code, @@ -20,8 +27,10 @@ internal val loadingFrameLayoutControllerFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("L") - custom { method, _ -> - method.containsLiteralInstruction(ic_offline_no_content_upside_down) - && method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible) - } + instructions( + resourceLiteral(ResourceType.DRAWABLE, "ic_offline_no_content_upside_down"), + resourceLiteral(ResourceType.STRING, "offline_no_content_body_text_not_offline_eligible"), + methodCall(name = "getString", returnType = "Ljava/lang/String;"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterImmediately()) + ) } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt index 2375c42111..9dab9c0dff 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt @@ -35,10 +35,10 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch( compatibleWith( YOUTUBE_PACKAGE_NAME( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/DisableHapticFeedbackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/DisableHapticFeedbackPatch.kt index 4977ea644b..fcccdf24f8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/DisableHapticFeedbackPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/DisableHapticFeedbackPatch.kt @@ -26,10 +26,10 @@ val disableHapticFeedbackPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt index fcd5298acf..b0ec1e64e5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt @@ -1,6 +1,8 @@ package app.revanced.patches.youtube.misc.imageurlhook +import app.revanced.patcher.anyInstruction import app.revanced.patcher.fingerprint +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags internal val onFailureFingerprint = fingerprint { @@ -45,7 +47,6 @@ internal const val CRONET_URL_REQUEST_CLASS_DESCRIPTOR = "Lorg/chromium/net/impl internal val requestFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") custom { _, classDef -> classDef.type == CRONET_URL_REQUEST_CLASS_DESCRIPTOR } @@ -60,5 +61,10 @@ internal val messageDigestImageUrlParentFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Ljava/lang/String;") parameters() - strings("@#&=*+-_.,:!?()/~'%;\$") + instructions( + anyInstruction( + string("@#&=*+-_.,:!?()/~'%;\$"), + string("@#&=*+-_.,:!?()/~'%;\$[]"), // 20.38+ + ) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt index 8d663ff2df..ed91b907f4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt @@ -7,17 +7,12 @@ import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater -import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.playservice.is_20_37_or_greater import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.TypeReference + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/BypassURLRedirectsPatch;" val bypassURLRedirectsPatch = bytecodePatch( name = "Bypass URL redirects", @@ -27,15 +22,14 @@ val bypassURLRedirectsPatch = bytecodePatch( sharedExtensionPatch, settingsPatch, addResourcesPatch, - versionCheckPatch, ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -46,41 +40,24 @@ val bypassURLRedirectsPatch = bytecodePatch( SwitchPreference("revanced_bypass_url_redirects"), ) - val fingerprints = if (is_19_33_or_greater) { - arrayOf( - abUriParserFingerprint, - httpUriParserFingerprint, - ) - } else { - arrayOf( - abUriParserLegacyFingerprint, - httpUriParserLegacyFingerprint, - ) - } - - fingerprints.forEach { - it.method.apply { - val insertIndex = findUriParseIndex() + arrayOf( + if (is_20_37_or_greater) { + (abUriParserFingerprint to 2) + } else { + (abUriParserLegacyFingerprint to 2) + }, + httpUriParserFingerprint to 0 + ).forEach { (fingerprint, index) -> + fingerprint.method.apply { + val insertIndex = fingerprint.instructionMatches[index].index val uriStringRegister = getInstruction(insertIndex).registerC replaceInstruction( insertIndex, - "invoke-static {v$uriStringRegister}," + - "Lapp/revanced/extension/youtube/patches/BypassURLRedirectsPatch;" + - "->" + - "parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;", + "invoke-static { v$uriStringRegister }, $EXTENSION_CLASS_DESCRIPTOR->" + + "parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;", ) } } } } - -internal fun Method.findUriParseIndex() = indexOfFirstInstruction { - val reference = getReference() - reference?.returnType == "Landroid/net/Uri;" && reference.name == "parse" -} - -internal fun Method.findWebViewCheckCastIndex() = indexOfFirstInstruction { - val reference = getReference() - opcode == Opcode.CHECK_CAST && reference?.type?.endsWith("/WebviewEndpointOuterClass${'$'}WebviewEndpoint;") == true -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt index 6f231693bd..bc2fba62e5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt @@ -1,68 +1,51 @@ package app.revanced.patches.youtube.misc.links +import app.revanced.patcher.StringComparisonType import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode /** - * Target 19.33+ + * 20.36 and lower. */ -internal val abUriParserFingerprint = fingerprint { +internal val abUriParserLegacyFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/Object") - parameters("Ljava/lang/Object") - custom { method, _ -> - method.findUriParseIndex() >= 0 && method.findWebViewCheckCastIndex() >= 0 - } + returns("Ljava/lang/Object;") + parameters("Ljava/lang/Object;") + instructions( + string("Found entityKey=`"), + string("that does not contain a PlaylistVideoEntityId", StringComparisonType.CONTAINS), + methodCall(smali = "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;") + ) } /** - * Target 19.33+ + * 20.37+ */ -internal val httpUriParserFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Landroid/net/Uri") - parameters("Ljava/lang/String") - strings("https", "://", "https:") - custom { methodDef, _ -> - methodDef.findUriParseIndex() >= 0 - } -} - -internal val abUriParserLegacyFingerprint = fingerprint { +internal val abUriParserFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/Object") - parameters("Ljava/lang/Object") - opcodes( - Opcode.RETURN_OBJECT, - Opcode.CHECK_CAST, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CHECK_CAST, - Opcode.RETURN_OBJECT, - Opcode.CHECK_CAST, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.RETURN_OBJECT, - Opcode.CHECK_CAST, + returns("Ljava/lang/Object;") + parameters("Ljava/lang/Object;") + instructions( + // Method is a switch statement of unrelated code, + // and there's no strings or anything unique to fingerprint. + methodCall(smali = "Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;"), + methodCall(smali = "Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;"), + methodCall(smali = "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;"), + methodCall(smali = "Ljava/util/List;->get(I)Ljava/lang/Object;"), ) - custom { methodDef, classDef -> - // This method is always called "a" because this kind of class always has a single (non-synthetic) method. - - if (methodDef.name != "a") return@custom false - - val count = classDef.methods.count() - count == 2 || count == 3 - } } -internal val httpUriParserLegacyFingerprint = fingerprint { +internal val httpUriParserFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Landroid/net/Uri") - parameters("Ljava/lang/String") - opcodes( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, + returns("Landroid/net/Uri;") + parameters("Ljava/lang/String;") + instructions( + methodCall(smali = "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;"), + string("https"), + string("://"), + string("https:"), ) - strings("://") } + diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt index 71916ecfb0..8dd2d4e278 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt @@ -41,10 +41,10 @@ val openLinksExternallyPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt index 497bd3c89b..185744d830 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt @@ -1,19 +1,18 @@ package app.revanced.patches.youtube.misc.litho.filter +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint import app.revanced.util.containsLiteralInstruction -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val componentContextParserFingerprint = fingerprint { - strings("Number of bits must be positive") -} - internal val componentCreateFingerprint = fingerprint { - strings( - "Element missing correct type extension", - "Element missing type" + instructions( + string("Element missing correct type extension"), + string("Element missing type") ) } @@ -25,6 +24,23 @@ internal val lithoFilterFingerprint = fingerprint { } internal val protobufBufferReferenceFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("[B") + instructions( + fieldAccess( + opcode = Opcode.IGET_OBJECT, + definingClass = "this", + type = "Lcom/google/android/libraries/elements/adl/UpbMessage;" + ), + methodCall( + definingClass = "Lcom/google/android/libraries/elements/adl/UpbMessage;", + name = "jniDecode" + ) + ) +} + +internal val protobufBufferReferenceLegacyFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("I", "Ljava/nio/ByteBuffer;") @@ -39,7 +55,9 @@ internal val protobufBufferReferenceFingerprint = fingerprint { internal val emptyComponentFingerprint = fingerprint { accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) parameters() - strings("EmptyComponent") + instructions( + string("EmptyComponent") + ) custom { _, classDef -> classDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 } @@ -50,7 +68,7 @@ internal val lithoThreadExecutorFingerprint = fingerprint { parameters("I", "I", "I") custom { method, classDef -> classDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" && - method.containsLiteralInstruction(1L) // 1L = default thread timeout. + method.containsLiteralInstruction(1L) // 1L = default thread timeout. } } @@ -58,12 +76,14 @@ internal val lithoComponentNameUpbFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") parameters() - literal { 45631264L } + instructions( + literal(45631264L) + ) } internal val lithoConverterBufferUpbFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("L") - parameters("L") - literal { 45419603L } + instructions( + literal(45419603L) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt index 81ae57cbb2..54949e77c0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt @@ -4,7 +4,6 @@ package app.revanced.patches.youtube.misc.litho.filter import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch @@ -12,18 +11,17 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_05_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_22_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.shared.conversionContextFingerprintToString import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.findFieldFromToString import app.revanced.util.findFreeRegister -import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.util.insertLiteralOverride +import app.revanced.util.returnLate import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.reference.FieldReference lateinit var addLithoFilter: (String) -> Unit private set @@ -65,11 +63,11 @@ val lithoFilterPatch = bytecodePatch( * } * } * - * class CreateComponentClass { - * public Component createComponent() { + * class ComponentContextParser { + * public Component parseComponent() { * ... * - * if (extensionClass.shouldFilter(identifier, path)) { // Inserted by this patch. + * if (extensionClass.shouldFilter()) { // Inserted by this patch. * return emptyComponent; * } * return originalUnpatchedComponent; // Original code. @@ -90,40 +88,41 @@ val lithoFilterPatch = bytecodePatch( invoke-direct { v1 }, $classDescriptor->()V const/16 v2, ${filterCount++} aput-object v1, v0, v2 - """, + """ ) } } // region Pass the buffer into extension. - protobufBufferReferenceFingerprint.method.addInstruction( + if (is_20_22_or_greater) { + // Hook method that bridges between UPB buffer native code and FB Litho. + // Method is found in 19.25+, but is forcefully turned off for 20.21 and lower. + protobufBufferReferenceFingerprint.let { + // Hook the buffer after the call to jniDecode(). + it.method.addInstruction( + it.instructionMatches.last().index + 1, + "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer([B)V", + ) + } + } + + // Legacy Non native buffer. + protobufBufferReferenceLegacyFingerprint.method.addInstruction( 0, "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", ) // endregion - // region Hook the method that parses bytes into a ComponentContext. - // Allow the method to run to completion, and override the - // return value with an empty component if it should be filtered. - // It is important to allow the original code to always run to completion, - // otherwise high memory usage and poor app performance can occur. + // region Modify the create component method and + // if the component is filtered then return an empty component. // Find the identifier/path fields of the conversion context. - val conversionContextIdentifierField = componentContextParserFingerprint.let { - // Identifier field is loaded just before the string declaration. - val index = it.method.indexOfFirstInstructionReversedOrThrow( - it.stringMatches!!.first().index - ) { - val reference = getReference() - reference?.definingClass == conversionContextFingerprintToString.originalClassDef.type - && reference.type == "Ljava/lang/String;" - } - it.method.getInstruction(index).getReference()!! - } + val conversionContextIdentifierField = conversionContextFingerprintToString.method + .findFieldFromToString("identifierProperty=") val conversionContextPathBuilderField = conversionContextFingerprintToString.originalClassDef .fields.single { field -> field.type == "Ljava/lang/StringBuilder;" } @@ -133,10 +132,8 @@ val lithoFilterPatch = bytecodePatch( // The only static method in the class. method -> AccessFlags.STATIC.isSet(method.accessFlags) } - val emptyComponentField = classBy { - // Only one field that matches. - it.type == builderMethodDescriptor.returnType - }!!.immutableClass.fields.single() + + val emptyComponentField = classBy(builderMethodDescriptor.returnType).fields.single() componentCreateFingerprint.method.apply { val insertIndex = if (is_19_17_or_greater) { @@ -154,6 +151,12 @@ val lithoFilterPatch = bytecodePatch( insertIndex, """ move-object/from16 v$freeRegister, p2 + + # 20.41 field is the abstract superclass. + # Verify it's the expected subclass just in case. + instance-of v$identifierRegister, v$freeRegister, ${conversionContextFingerprintToString.classDef.type} + if-eqz v$identifierRegister, :unfiltered + iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->isFiltered(Ljava/lang/String;Ljava/lang/StringBuilder;)Z @@ -199,23 +202,17 @@ val lithoFilterPatch = bytecodePatch( // Flag was removed in 20.05. It appears a new flag might be used instead (45660109L), // but if the flag is forced on then litho filtering still works correctly. if (is_19_25_or_greater && !is_20_05_or_greater) { - lithoComponentNameUpbFeatureFlagFingerprint.method.apply { - // Don't use return early, so the debug patch logs if this was originally on. - val insertIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN) - val register = getInstruction(insertIndex).registerA - - addInstruction(insertIndex, "const/4 v$register, 0x0") - } + lithoComponentNameUpbFeatureFlagFingerprint.method.returnLate(false) } // Turn off a feature flag that enables native code of protobuf parsing (Upb protobuf). - // If this is enabled, then the litho protobuffer hook will always show an empty buffer - // since it's no longer handled by the hooked Java code. - lithoConverterBufferUpbFeatureFlagFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT) - val register = getInstruction(index).registerA - - addInstruction(index + 1, "const/4 v$register, 0x0") + lithoConverterBufferUpbFeatureFlagFingerprint.let { + // 20.22 the flag is still enabled in one location, but what it does is not known. + // Disable it anyway. + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + false + ) } // endregion @@ -224,4 +221,4 @@ val lithoFilterPatch = bytecodePatch( finalize { lithoFilterFingerprint.method.replaceInstruction(0, "const/16 v0, $filterCount") } -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/Fingerprints.kt new file mode 100644 index 0000000000..3101e61dcb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/Fingerprints.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.youtube.misc.loopvideo + +import app.revanced.patcher.fingerprint +import app.revanced.patcher.string +import com.android.tools.smali.dexlib2.AccessFlags + +internal val videoStartPlaybackFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + instructions( + string("play() called when the player wasn't loaded."), + string("play() blocked because Background Playability failed") + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/LoopVideoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/LoopVideoPatch.kt index bd5feee5a5..1e87c0d4a6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/LoopVideoPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/LoopVideoPatch.kt @@ -7,8 +7,8 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.loopvideo.button.loopVideoButtonPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen -import app.revanced.patches.youtube.shared.loopVideoFingerprint -import app.revanced.patches.youtube.shared.loopVideoParentFingerprint +import app.revanced.patches.youtube.video.information.videoEndMethod +import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.indexOfFirstInstructionReversedOrThrow import com.android.tools.smali.dexlib2.Opcode @@ -22,15 +22,16 @@ val loopVideoPatch = bytecodePatch( dependsOn( sharedExtensionPatch, addResourcesPatch, - loopVideoButtonPatch + loopVideoButtonPatch, + videoInformationPatch ) compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) @@ -41,9 +42,10 @@ val loopVideoPatch = bytecodePatch( SwitchPreference("revanced_loop_video"), ) - loopVideoFingerprint.match(loopVideoParentFingerprint.originalClassDef).method.apply { - val playMethod = loopVideoParentFingerprint.method - val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.RETURN_VOID) + videoEndMethod.apply { + // Add call to start playback again, but must not allow exit fullscreen patch call + // to be reached if the video is looped. + val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.INVOKE_VIRTUAL) + 1 addInstructionsAtControlFlowLabel( insertIndex, @@ -51,7 +53,8 @@ val loopVideoPatch = bytecodePatch( invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldLoopVideo()Z move-result v0 if-eqz v0, :do_not_loop - invoke-virtual { p0 }, $playMethod + invoke-virtual { p0 }, ${videoStartPlaybackFingerprint.method} + return-void :do_not_loop nop """ diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt index a5fa0ed5e2..f3732dbb4d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt @@ -1,24 +1,32 @@ package app.revanced.patches.youtube.misc.navigation +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import app.revanced.patches.youtube.layout.buttons.navigation.navigationButtonsPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal val actionBarSearchResultsFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Landroid/view/View;") - literal { actionBarSearchResultsViewMicId } + instructions( + resourceLiteral(ResourceType.LAYOUT, "action_bar_search_results_view_mic"), + methodCall(name = "setLayoutDirection") + ) } internal val toolbarLayoutFingerprint = fingerprint { accessFlags(AccessFlags.PROTECTED, AccessFlags.CONSTRUCTOR) - literal { toolbarContainerId } + instructions( + resourceLiteral(ResourceType.ID, "toolbar_container"), + checkCast("Lcom/google/android/apps/youtube/app/ui/actionbar/MainCollapsingToolbarLayout;") + ) } /** @@ -28,7 +36,7 @@ internal val appCompatToolbarBackButtonFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Landroid/graphics/drawable/Drawable;") parameters() - custom { methodDef, classDef -> + custom { _, classDef -> classDef.type == "Landroid/support/v7/widget/Toolbar;" } } @@ -39,7 +47,9 @@ internal val appCompatToolbarBackButtonFingerprint = fingerprint { internal val initializeButtonsFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") - literal { imageOnlyTabResourceId } + instructions( + resourceLiteral(ResourceType.LAYOUT, "image_only_tab") + ) } /** @@ -70,6 +80,10 @@ internal val navigationEnumFingerprint = fingerprint { "VIDEO_LIBRARY_WHITE", "INCOGNITO_CIRCLE", ) + custom { _, classDef -> + // Don't match our own code. + !classDef.type.startsWith("Lapp/revanced") + } } internal val pivotBarButtonsCreateDrawableViewFingerprint = fingerprint { @@ -77,12 +91,12 @@ internal val pivotBarButtonsCreateDrawableViewFingerprint = fingerprint { returns("Landroid/view/View;") custom { method, _ -> method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" && - // Only one method has a Drawable parameter. + // Only one view creation method has a Drawable parameter. method.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;" } } -internal val pivotBarButtonsCreateResourceViewFingerprint = fingerprint { +internal val pivotBarButtonsCreateResourceStyledViewFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Landroid/view/View;") parameters("L", "Z", "I", "L") @@ -91,33 +105,50 @@ internal val pivotBarButtonsCreateResourceViewFingerprint = fingerprint { } } -internal fun indexOfSetViewSelectedInstruction(method: Method) = method.indexOfFirstInstruction { - opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setSelected" +/** + * 20.21+ + */ +internal val pivotBarButtonsCreateResourceIntViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/view/View;") + custom { method, _ -> + method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" && + // Only one view creation method has an int first parameter. + method.parameterTypes.firstOrNull() == "I" + } } internal val pivotBarButtonsViewSetSelectedFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("I", "Z") + instructions( + methodCall(name = "setSelected") + ) custom { method, _ -> - indexOfSetViewSelectedInstruction(method) >= 0 && - method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" + method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" } } internal val pivotBarConstructorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - strings("com.google.android.apps.youtube.app.endpoint.flags") + instructions( + string("com.google.android.apps.youtube.app.endpoint.flags"), + ) } internal val imageEnumConstructorFingerprint = fingerprint { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - strings("TAB_ACTIVITY_CAIRO") + instructions( + string("TAB_ACTIVITY_CAIRO"), + opcode(Opcode.SPUT_OBJECT) + ) } internal val setEnumMapFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - literal { - ytFillBellId - } + instructions( + resourceLiteral(ResourceType.DRAWABLE, "yt_fill_bell_black_24"), + methodCall(smali = "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;", location = MatchAfterWithin(10)), + methodCall(smali = "Ljava/util/EnumMap;->put(Ljava/lang/Enum;Ljava/lang/Object;)Ljava/lang/Object;", location = MatchAfterWithin(10)) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt index 083fdf2e2e..25c4f09423 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt @@ -9,17 +9,19 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.playservice.is_19_35_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_21_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_28_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_39_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.shared.mainActivityOnBackPressedFingerprint +import app.revanced.util.ResourceGroup +import app.revanced.util.copyResources +import app.revanced.util.findFreeRegister import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation @@ -28,29 +30,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.TypeReference import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.util.MethodUtil - -internal var imageOnlyTabResourceId = -1L - private set -internal var actionBarSearchResultsViewMicId = -1L - private set -internal var ytFillBellId = -1L - private set -internal var toolbarContainerId = -1L - private set - -private val navigationBarHookResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - - execute { - imageOnlyTabResourceId = resourceMappings["layout", "image_only_tab"] - actionBarSearchResultsViewMicId = resourceMappings["layout", "action_bar_search_results_view_mic"] - ytFillBellId = resourceMappings["drawable", "yt_fill_bell_black_24"] - toolbarContainerId = resourceMappings["id", "toolbar_container"] - } -} +import java.util.logging.Logger internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/shared/NavigationBar;" @@ -64,12 +46,25 @@ lateinit var hookNavigationButtonCreated: (String) -> Unit val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navigation or search bar.") { dependsOn( sharedExtensionPatch, - navigationBarHookResourcePatch, + versionCheckPatch, playerTypeHookPatch, // Required to detect the search bar in all situations. + resourceMappingPatch, // Used by fingerprints + resourcePatch { + // Copy missing notification icon. + execute { + copyResources( + "navigationbuttons", + ResourceGroup( + "drawable", + "revanced_fill_bell_cairo_black_24.xml" + ) + ) + } + } ) execute { - fun MutableMethod.addHook(hook: Hook, insertPredicate: Instruction.() -> Boolean) { + fun MutableMethod.addHook(hook: NavigationHook, insertPredicate: Instruction.() -> Boolean) { val filtered = instructions.filter(insertPredicate) if (filtered.isEmpty()) throw PatchException("Could not find insert indexes") filtered.forEach { @@ -87,22 +82,32 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig initializeButtonsFingerprint.match(pivotBarConstructorFingerprint.originalClassDef).method.apply { // Hook the current navigation bar enum value. Note, the 'You' tab does not have an enum value. val navigationEnumClassName = navigationEnumFingerprint.classDef.type - addHook(Hook.SET_LAST_APP_NAVIGATION_ENUM) { + addHook(NavigationHook.SET_LAST_APP_NAVIGATION_ENUM) { opcode == Opcode.INVOKE_STATIC && getReference()?.definingClass == navigationEnumClassName } // Hook the creation of navigation tab views. val drawableTabMethod = pivotBarButtonsCreateDrawableViewFingerprint.method - addHook(Hook.NAVIGATION_TAB_LOADED) predicate@{ + addHook(NavigationHook.NAVIGATION_TAB_LOADED) predicate@{ MethodUtil.methodSignaturesMatch( getReference() ?: return@predicate false, drawableTabMethod, ) } - val imageResourceTabMethod = pivotBarButtonsCreateResourceViewFingerprint.originalMethod - addHook(Hook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{ + if (is_20_21_or_greater && !is_20_28_or_greater) { + val imageResourceIntTabMethod = pivotBarButtonsCreateResourceIntViewFingerprint.originalMethod + addHook(NavigationHook.NAVIGATION_TAB_LOADED) predicate@{ + MethodUtil.methodSignaturesMatch( + getReference() ?: return@predicate false, + imageResourceIntTabMethod, + ) + } + } + + val imageResourceTabMethod = pivotBarButtonsCreateResourceStyledViewFingerprint.originalMethod + addHook(NavigationHook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{ MethodUtil.methodSignaturesMatch( getReference() ?: return@predicate false, imageResourceTabMethod, @@ -110,17 +115,19 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig } } - pivotBarButtonsViewSetSelectedFingerprint.method.apply { - val index = indexOfSetViewSelectedInstruction(this) - val instruction = getInstruction(index) - val viewRegister = instruction.registerC - val isSelectedRegister = instruction.registerD + pivotBarButtonsViewSetSelectedFingerprint.let { + it.method.apply { + val index = it.instructionMatches.first().index + val instruction = getInstruction(index) + val viewRegister = instruction.registerC + val isSelectedRegister = instruction.registerD - addInstruction( - index + 1, - "invoke-static { v$viewRegister, v$isSelectedRegister }, " + - "$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V", - ) + addInstruction( + index + 1, + "invoke-static { v$viewRegister, v$isSelectedRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V", + ) + } } // Hook onto back button pressed. Needed to fix race problem with @@ -135,37 +142,31 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig // Two different layouts are used at the hooked code. // Insert before the first ViewGroup method call after inflating, // so this works regardless which layout is used. - actionBarSearchResultsFingerprint.method.apply { - val searchBarResourceId = indexOfFirstLiteralInstructionOrThrow( - actionBarSearchResultsViewMicId, - ) + actionBarSearchResultsFingerprint.let { + it.method.apply { + val instructionIndex = it.instructionMatches.last().index + val viewRegister = getInstruction(instructionIndex).registerC - val instructionIndex = indexOfFirstInstructionOrThrow(searchBarResourceId) { - opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setLayoutDirection" + addInstruction( + instructionIndex, + "invoke-static { v$viewRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V", + ) } - - val viewRegister = getInstruction(instructionIndex).registerC - - addInstruction( - instructionIndex, - "invoke-static { v$viewRegister }, " + - "$EXTENSION_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V", - ) } // Hook the back button visibility. - toolbarLayoutFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow { - opcode == Opcode.CHECK_CAST && getReference()?.type == - "Lcom/google/android/apps/youtube/app/ui/actionbar/MainCollapsingToolbarLayout;" - } - val register = getInstruction(index).registerA + toolbarLayoutFingerprint.let { + it.method.apply { + val index = it.instructionMatches.last().index + val register = getInstruction(index).registerA - addInstruction( - index + 1, - "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setToolbar(Landroid/widget/FrameLayout;)V" - ) + addInstruction( + index + 1, + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setToolbar(Landroid/widget/FrameLayout;)V" + ) + } } // Add interface for extensions code to call obfuscated methods. @@ -209,39 +210,38 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig ) } - // Fix YT bug of notification tab missing the filled icon. - if (is_19_35_or_greater) { - val cairoNotificationEnumReference = with(imageEnumConstructorFingerprint) { - val stringIndex = stringMatches!!.first().index - val cairoNotificationEnumIndex = method.indexOfFirstInstructionOrThrow(stringIndex) { - opcode == Opcode.SPUT_OBJECT - } - method.getInstruction(cairoNotificationEnumIndex).reference - } + if (is_20_39_or_greater) { + return@execute Logger.getLogger(this::class.java.name).warning( + "20.39+ Navigation tab activity button selected state is not yet fixed." + ) + } - setEnumMapFingerprint.method.apply { - val enumMapIndex = indexOfFirstInstructionReversedOrThrow { - val reference = getReference() - opcode == Opcode.INVOKE_VIRTUAL && - reference?.definingClass == "Ljava/util/EnumMap;" && - reference.name == "put" && - reference.parameterTypes.firstOrNull() == "Ljava/lang/Enum;" + // Fix YT bug of notification tab missing the filled icon. + if (is_19_35_or_greater && !is_20_39_or_greater) { // FIXME: 20.39+ needs this fix. + val cairoNotificationEnumReference = imageEnumConstructorFingerprint + .instructionMatches.last().getInstruction().reference + + setEnumMapFingerprint.let { + it.method.apply { + val setEnumIntegerIndex = it.instructionMatches.last().index + val enumMapRegister = getInstruction(setEnumIntegerIndex).registerC + val insertIndex = setEnumIntegerIndex + 1 + val freeRegister = findFreeRegister(insertIndex, enumMapRegister) + + addInstructions( + insertIndex, + """ + sget-object v$freeRegister, $cairoNotificationEnumReference + invoke-static { v$enumMapRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V + """ + ) } - val instruction = getInstruction(enumMapIndex) - - addInstructions( - enumMapIndex + 1, - """ - sget-object v${instruction.registerD}, $cairoNotificationEnumReference - invoke-static { v${instruction.registerC}, v${instruction.registerD} }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V - """ - ) } } } } -private enum class Hook(val methodName: String, val parameters: String) { +private enum class NavigationHook(val methodName: String, val parameters: String) { SET_LAST_APP_NAVIGATION_ENUM("setLastAppNavigationEnum", "Ljava/lang/Enum;"), NAVIGATION_TAB_LOADED("navigationTabLoaded", "Landroid/view/View;"), NAVIGATION_IMAGE_RESOURCE_TAB_LOADED("navigationImageResourceTabLoaded", "Landroid/view/View;"), diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt index da21371350..91c36f6cf0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt @@ -1,25 +1,15 @@ package app.revanced.patches.youtube.misc.playercontrols +import app.revanced.patcher.InstructionLocation.MatchAfterImmediately +import app.revanced.patcher.checkCast import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import app.revanced.util.indexOfFirstInstructionReversed -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.reference.MethodReference - -internal fun indexOfFocusableInTouchModeInstruction(method: Method) = - method.indexOfFirstInstruction { - getReference()?.name == "setFocusableInTouchMode" - } - -internal fun indexOfTranslationInstruction(method: Method) = - method.indexOfFirstInstructionReversed { - getReference()?.name == "setTranslationY" - } internal val playerControlsVisibilityEntityModelFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC) @@ -35,30 +25,21 @@ internal val playerControlsVisibilityEntityModelFingerprint = fingerprint { } internal val youtubeControlsOverlayFingerprint = fingerprint { - accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) returns("V") parameters() - custom { method, _ -> - indexOfFocusableInTouchModeInstruction(method) >= 0 && - method.containsLiteralInstruction(inset_overlay_view_layout_id) && - method.containsLiteralInstruction(scrim_overlay_id) - - } + instructions( + methodCall(name = "setFocusableInTouchMode"), + resourceLiteral(ResourceType.ID, "inset_overlay_view_layout"), + resourceLiteral(ResourceType.ID, "scrim_overlay"), + ) } internal val motionEventFingerprint = fingerprint { returns("V") parameters("Landroid/view/MotionEvent;") - custom { method, _ -> - indexOfTranslationInstruction(method) >= 0 - } -} - -internal val playerTopControlsInflateFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - literal { controls_layout_stub_id } + instructions( + methodCall(name = "setTranslationY") + ) } internal val playerControlsExtensionHookListenersExistFingerprint = fingerprint { @@ -81,20 +62,36 @@ internal val playerControlsExtensionHookFingerprint = fingerprint { } } +internal val playerTopControlsInflateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + instructions( + resourceLiteral(ResourceType.ID, "controls_layout_stub"), + methodCall("Landroid/view/ViewStub;", "inflate"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterImmediately()) + ) +} + internal val playerBottomControlsInflateFingerprint = fingerprint { returns("Ljava/lang/Object;") parameters() - literal { bottom_ui_container_stub_id } + instructions( + resourceLiteral(ResourceType.ID, "bottom_ui_container_stub"), + methodCall("Landroid/view/ViewStub;", "inflate"), + opcode(Opcode.MOVE_RESULT_OBJECT, MatchAfterImmediately()) + ) } internal val overlayViewInflateFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("Landroid/view/View;") - custom { methodDef, _ -> - methodDef.containsLiteralInstruction(fullscreen_button_id) && - methodDef.containsLiteralInstruction(heatseeker_viewstub_id) - } + instructions( + resourceLiteral(ResourceType.ID, "heatseeker_viewstub"), + resourceLiteral(ResourceType.ID, "fullscreen_button"), + checkCast("Landroid/widget/ImageView;") + ) } /** @@ -110,13 +107,43 @@ internal val playerBottomControlsExploderFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") parameters() - literal { 45643739L } + instructions( + literal(45643739L) + ) } internal val playerTopControlsExperimentalLayoutFeatureFlagFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("I") parameters() - literal { 45629424L } + instructions( + literal(45629424L) + ) } +internal val playerControlsLargeOverlayButtonsFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + instructions( + literal(45709810L) + ) +} + +internal val playerControlsFullscreenLargeButtonsFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + instructions( + literal(45686474L) + ) +} + +internal val playerControlsButtonStrokeFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + instructions( + literal(45713296) + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatch.kt index e10725f767..ab0378e4ad 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatch.kt @@ -12,13 +12,13 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction private const val EXTENSION_PLAYER_CONTROLS_VISIBILITY_HOOK_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerControlsVisibilityHookPatch;" -val PlayerControlsOverlayVisibilityPatch = bytecodePatch { +val playerControlsOverlayVisibilityPatch = bytecodePatch { dependsOn(sharedExtensionPatch) execute { playerControlsVisibilityEntityModelFingerprint.let { it.method.apply { - val startIndex = it.patternMatch!!.startIndex + val startIndex = it.instructionMatches.first().index val iGetReference = getInstruction(startIndex).reference val staticReference = getInstruction(startIndex + 1).reference diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt index e5949586de..6af9b1a132 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt @@ -7,17 +7,24 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.Document import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_35_or_greater -import app.revanced.util.* +import app.revanced.patches.youtube.misc.playservice.is_20_19_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_20_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_28_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_30_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.util.copyXmlNode +import app.revanced.util.findElementByAttributeValue +import app.revanced.util.findElementByAttributeValueOrThrow +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.inputStreamFromBundledResource +import app.revanced.util.returnEarly +import app.revanced.util.returnLate import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.TypeReference import org.w3c.dom.Node /** @@ -39,22 +46,7 @@ internal lateinit var addTopControl: (String) -> Unit lateinit var addBottomControl: (String) -> Unit private set -internal var bottom_ui_container_stub_id = -1L - private set -internal var controls_layout_stub_id = -1L - private set -internal var heatseeker_viewstub_id = -1L - private set -internal var fullscreen_button_id = -1L - private set -internal var inset_overlay_view_layout_id = -1L - private set -internal var scrim_overlay_id = -1L - private set - -val playerControlsResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - +internal val playerControlsResourcePatch = resourcePatch { /** * The element to the left of the element being added. */ @@ -68,13 +60,6 @@ val playerControlsResourcePatch = resourcePatch { execute { val targetResourceName = "youtube_controls_bottom_ui_container.xml" - bottom_ui_container_stub_id = resourceMappings["id", "bottom_ui_container_stub"] - controls_layout_stub_id = resourceMappings["id", "controls_layout_stub"] - heatseeker_viewstub_id = resourceMappings["id", "heatseeker_viewstub"] - fullscreen_button_id = resourceMappings["id", "fullscreen_button"] - inset_overlay_view_layout_id = resourceMappings["id", "inset_overlay_view_layout"] - scrim_overlay_id = resourceMappings["id", "scrim_overlay"] - bottomTargetDocument = document("res/layout/$targetResourceName") val bottomTargetElement: Node = bottomTargetDocument.getElementsByTagName( @@ -87,6 +72,18 @@ val playerControlsResourcePatch = resourcePatch { bottomLastLeftOf, ) + // Modify the fullscreen button stub attributes for correct positioning. + // The fullscreen button is lower than the ReVanced buttons (unpatched app bug). + // Issue is only present in later app targets, but this change seems to + // do no harm to earlier releases. + bottomTargetDocumentChildNodes.findElementByAttributeValueOrThrow( + "android:id", + "@id/youtube_controls_fullscreen_button_stub" + ).apply { + setAttribute("android:layout_marginBottom", "6.0dip") + setAttribute("android:layout_width", "48.0dip") + } + addTopControl = { resourceDirectoryName -> val resourceFileName = "host/layout/youtube_controls_layout.xml" val hostingResourceStream = inputStreamFromBundledResource( @@ -189,16 +186,16 @@ fun initializeBottomControl(descriptor: String) { * @param descriptor The descriptor of the method which should be called. */ fun injectVisibilityCheckCall(descriptor: String) { - visibilityMethod.addInstruction( - visibilityInsertIndex++, - "invoke-static { p1 , p2 }, $descriptor->setVisibility(ZZ)V", - ) - if (!visibilityImmediateCallbacksExistModified) { visibilityImmediateCallbacksExistModified = true visibilityImmediateCallbacksExistMethod.returnEarly(true) } + visibilityMethod.addInstruction( + visibilityInsertIndex++, + "invoke-static { p1 , p2 }, $descriptor->setVisibility(ZZ)V", + ) + visibilityImmediateMethod.addInstruction( visibilityImmediateInsertIndex++, "invoke-static { p0 }, $descriptor->setVisibilityImmediate(Z)V", @@ -216,24 +213,24 @@ internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;" private lateinit var inflateTopControlMethod: MutableMethod -private var inflateTopControlInsertIndex: Int = -1 -private var inflateTopControlRegister: Int = -1 +private var inflateTopControlInsertIndex = -1 +private var inflateTopControlRegister = -1 private lateinit var inflateBottomControlMethod: MutableMethod -private var inflateBottomControlInsertIndex: Int = -1 -private var inflateBottomControlRegister: Int = -1 +private var inflateBottomControlInsertIndex = -1 +private var inflateBottomControlRegister = -1 -private lateinit var visibilityMethod: MutableMethod -private var visibilityInsertIndex: Int = 0 - -private var visibilityImmediateCallbacksExistModified = false private lateinit var visibilityImmediateCallbacksExistMethod : MutableMethod +private var visibilityImmediateCallbacksExistModified = false + +private lateinit var visibilityMethod: MutableMethod +private var visibilityInsertIndex = 0 private lateinit var visibilityImmediateMethod: MutableMethod -private var visibilityImmediateInsertIndex: Int = 0 +private var visibilityImmediateInsertIndex = 0 private lateinit var visibilityNegatedImmediateMethod: MutableMethod -private var visibilityNegatedImmediateInsertIndex: Int = 0 +private var visibilityNegatedImmediateInsertIndex = 0 val playerControlsPatch = bytecodePatch( description = "Manages the code for the player controls of the YouTube player.", @@ -241,30 +238,30 @@ val playerControlsPatch = bytecodePatch( dependsOn( playerControlsResourcePatch, sharedExtensionPatch, - PlayerControlsOverlayVisibilityPatch + resourceMappingPatch, // Used by fingerprints. + playerControlsOverlayVisibilityPatch, + versionCheckPatch ) execute { - fun MutableMethod.indexOfFirstViewInflateOrThrow() = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.definingClass == "Landroid/view/ViewStub;" && - reference.name == "inflate" - } - - playerBottomControlsInflateFingerprint.method.apply { - inflateBottomControlMethod = this + playerBottomControlsInflateFingerprint.let { + it.method.apply { + inflateBottomControlMethod = this - val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1 - inflateBottomControlRegister = getInstruction(inflateReturnObjectIndex).registerA - inflateBottomControlInsertIndex = inflateReturnObjectIndex + 1 + val inflateReturnObjectIndex = it.instructionMatches.last().index + inflateBottomControlRegister = getInstruction(inflateReturnObjectIndex).registerA + inflateBottomControlInsertIndex = inflateReturnObjectIndex + 1 + } } - playerTopControlsInflateFingerprint.method.apply { - inflateTopControlMethod = this + playerTopControlsInflateFingerprint.let { + it.method.apply { + inflateTopControlMethod = this - val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1 - inflateTopControlRegister = getInstruction(inflateReturnObjectIndex).registerA - inflateTopControlInsertIndex = inflateReturnObjectIndex + 1 + val inflateReturnObjectIndex = it.instructionMatches.last().index + inflateTopControlRegister = getInstruction(inflateReturnObjectIndex).registerA + inflateTopControlInsertIndex = inflateReturnObjectIndex + 1 + } } visibilityMethod = controlsOverlayVisibilityFingerprint.match( @@ -273,29 +270,25 @@ val playerControlsPatch = bytecodePatch( // Hook the fullscreen close button. Used to fix visibility // when seeking and other situations. - overlayViewInflateFingerprint.method.apply { - val resourceIndex = indexOfFirstLiteralInstructionReversedOrThrow(fullscreen_button_id) + overlayViewInflateFingerprint.let { + it.method.apply { + val index = it.instructionMatches.last().index + val register = getInstruction(index).registerA - val index = indexOfFirstInstructionOrThrow(resourceIndex) { - opcode == Opcode.CHECK_CAST && - getReference()?.type == - "Landroid/widget/ImageView;" + addInstruction( + index + 1, + "invoke-static { v$register }, " + + "$EXTENSION_CLASS_DESCRIPTOR->setFullscreenCloseButton(Landroid/widget/ImageView;)V", + ) } - val register = getInstruction(index).registerA - - addInstruction( - index + 1, - "invoke-static { v$register }, " + - "$EXTENSION_CLASS_DESCRIPTOR->setFullscreenCloseButton(Landroid/widget/ImageView;)V", - ) } visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistFingerprint.method visibilityImmediateMethod = playerControlsExtensionHookFingerprint.method - motionEventFingerprint.match(youtubeControlsOverlayFingerprint.originalClassDef).method.apply { - visibilityNegatedImmediateMethod = this - visibilityNegatedImmediateInsertIndex = indexOfTranslationInstruction(this) + 1 + motionEventFingerprint.match(youtubeControlsOverlayFingerprint.originalClassDef).let { + visibilityNegatedImmediateMethod = it.method + visibilityNegatedImmediateInsertIndex = it.instructionMatches.first().index + 1 } // A/B test for a slightly different bottom overlay controls, @@ -306,24 +299,30 @@ val playerControlsPatch = bytecodePatch( playerBottomControlsExploderFeatureFlagFingerprint.method.returnLate(false) } - // A/B test of new top overlay controls. Two different layouts can be used: + // A/B test of different top overlay controls. Two different layouts can be used: // youtube_cf_navigation_improvement_controls_layout.xml // youtube_cf_minimal_impact_controls_layout.xml // - // Visually there is no noticeable difference between either of these compared to the default. - // There is additional logic that is active when youtube_cf_navigation_improvement_controls_layout - // is active, but what it does is not entirely clear. - // - // For now force this a/b feature off as it breaks the top player buttons. - if (is_19_25_or_greater) { + // Flag was removed in 20.19+ + if (is_19_25_or_greater && !is_20_19_or_greater) { playerTopControlsExperimentalLayoutFeatureFlagFingerprint.method.apply { val index = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_OBJECT) val register = getInstruction(index).registerA - addInstruction( - index + 1, - "const-string v$register, \"default\"" - ) + addInstruction(index + 1, "const-string v$register, \"default\"") + } + } + + // Turn off a/b tests of ugly player buttons that don't match the style of custom player buttons. + if (is_20_20_or_greater) { + playerControlsFullscreenLargeButtonsFeatureFlagFingerprint.method.returnLate(false) + + if (is_20_28_or_greater) { + playerControlsLargeOverlayButtonsFeatureFlagFingerprint.method.returnLate(false) + } + + if (is_20_30_or_greater) { + playerControlsButtonStrokeFeatureFlagFingerprint.method.returnLate(false) } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt index 522d06b204..308445ca9b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt @@ -1,35 +1,60 @@ package app.revanced.patches.youtube.misc.playertype +import app.revanced.patcher.InstructionLocation.MatchAfterWithin import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val playerTypeFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - opcodes( - Opcode.IF_NE, - Opcode.RETURN_VOID, +internal val playerTypeEnumFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + strings( + "NONE", + "HIDDEN", + "WATCH_WHILE_MINIMIZED", + "WATCH_WHILE_MAXIMIZED", + "WATCH_WHILE_FULLSCREEN", + "WATCH_WHILE_SLIDING_MAXIMIZED_FULLSCREEN", + "WATCH_WHILE_SLIDING_MINIMIZED_MAXIMIZED", + "WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED", + "INLINE_MINIMAL", + "VIRTUAL_REALITY_FULLSCREEN", + "WATCH_WHILE_PICTURE_IN_PICTURE", ) - custom { _, classDef -> classDef.endsWith("/YouTubePlayerOverlaysLayout;") } } internal val reelWatchPagerFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Landroid/view/View;") - literal { reelWatchPlayerId } + instructions( + resourceLiteral(ResourceType.ID, "reel_watch_player"), + opcode(Opcode.MOVE_RESULT_OBJECT, location = MatchAfterWithin(10)) + ) +} + +internal val videoStateEnumFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + parameters() + strings( + "NEW", + "PLAYING", + "PAUSED", + "RECOVERABLE_ERROR", + "UNRECOVERABLE_ERROR", + "ENDED" + ) } -internal val videoStateFingerprint = fingerprint { +// 20.33 and lower class name ControlsState. 20.34+ class name is obfuscated. +internal val controlsStateToStringFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Lcom/google/android/libraries/youtube/player/features/overlay/controls/ControlsState;") - opcodes( - Opcode.CONST_4, - Opcode.IF_EQZ, - Opcode.IF_EQZ, - Opcode.IGET_OBJECT, // obfuscated parameter field name + parameters() + returns("Ljava/lang/String;") + instructions( + string("videoState"), + string("isBuffering") ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt index b14de0cbea..d31906239c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt @@ -3,64 +3,82 @@ package app.revanced.patches.youtube.misc.playertype import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.fieldAccess +import app.revanced.patcher.fingerprint import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow -import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerTypeHookPatch;" -internal var reelWatchPlayerId = -1L - private set - -private val playerTypeHookResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - - execute { - reelWatchPlayerId = resourceMappings["id", "reel_watch_player"] - } -} - val playerTypeHookPatch = bytecodePatch( description = "Hook to get the current player type and video playback state.", ) { - dependsOn(sharedExtensionPatch, playerTypeHookResourcePatch) + dependsOn(sharedExtensionPatch, resourceMappingPatch) execute { - playerTypeFingerprint.method.addInstruction( + val playerOverlaysSetPlayerTypeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters(playerTypeEnumFingerprint.originalClassDef.type) + custom { _, classDef -> + classDef.endsWith("/YouTubePlayerOverlaysLayout;") + } + } + + playerOverlaysSetPlayerTypeFingerprint.method.addInstruction( 0, - "invoke-static {p1}, $EXTENSION_CLASS_DESCRIPTOR->setPlayerType(Ljava/lang/Enum;)V", + "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->setPlayerType(Ljava/lang/Enum;)V", ) - reelWatchPagerFingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(reelWatchPlayerId) - val registerIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT_OBJECT) - val viewRegister = getInstruction(registerIndex).registerA + reelWatchPagerFingerprint.let { + it.method.apply { + val index = it.instructionMatches.last().index + val register = getInstruction(index).registerA - addInstruction( - registerIndex + 1, - "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR->onShortsCreate(Landroid/view/View;)V" - ) + addInstruction( + index + 1, + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->onShortsCreate(Landroid/view/View;)V" + ) + } } - videoStateFingerprint.method.apply { - val endIndex = videoStateFingerprint.patternMatch!!.endIndex - val videoStateFieldName = getInstruction(endIndex).reference + val controlStateType = controlsStateToStringFingerprint.originalClassDef.type - addInstructions( - 0, - """ - iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field - invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->setVideoState(Ljava/lang/Enum;)V - """ + val videoStateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters(controlStateType) + instructions( + // Obfuscated parameter field name. + fieldAccess( + definingClass = controlStateType, + type = videoStateEnumFingerprint.originalClassDef.type + ), + resourceLiteral(ResourceType.STRING, "accessibility_play"), + resourceLiteral(ResourceType.STRING, "accessibility_pause") ) } + + videoStateFingerprint.let { + it.method.apply { + val videoStateFieldName = getInstruction( + it.instructionMatches.first().index + ).reference + + addInstructions( + 0, + """ + iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field + invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->setVideoState(Ljava/lang/Enum;)V + """ + ) + } + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt index 133beddfb3..ff131156af 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt @@ -8,48 +8,43 @@ import kotlin.properties.Delegates // Use notNull delegate so an exception is thrown if these fields are accessed before they are set. -@Deprecated("19.34.42 is the lowest supported version") -var is_19_03_or_greater : Boolean by Delegates.notNull() - private set -@Deprecated("19.34.42 is the lowest supported version") -var is_19_04_or_greater : Boolean by Delegates.notNull() - private set -@Deprecated("19.34.42 is the lowest supported version") -var is_19_16_or_greater : Boolean by Delegates.notNull() - private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_17_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_18_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_23_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_25_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_26_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_29_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_32_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_33_or_greater : Boolean by Delegates.notNull() private set -@Deprecated("19.34.42 is the lowest supported version") +@Deprecated("19.43.41 is the lowest supported version") var is_19_34_or_greater : Boolean by Delegates.notNull() private set +@Deprecated("19.43.41 is the lowest supported version") var is_19_35_or_greater : Boolean by Delegates.notNull() private set +@Deprecated("19.43.41 is the lowest supported version") var is_19_36_or_greater : Boolean by Delegates.notNull() private set +@Deprecated("19.43.41 is the lowest supported version") var is_19_41_or_greater : Boolean by Delegates.notNull() private set +@Deprecated("19.43.41 is the lowest supported version") var is_19_43_or_greater : Boolean by Delegates.notNull() private set var is_19_46_or_greater : Boolean by Delegates.notNull() @@ -74,6 +69,34 @@ var is_20_14_or_greater : Boolean by Delegates.notNull() private set var is_20_15_or_greater : Boolean by Delegates.notNull() private set +var is_20_19_or_greater : Boolean by Delegates.notNull() + private set +var is_20_20_or_greater : Boolean by Delegates.notNull() + private set +var is_20_21_or_greater : Boolean by Delegates.notNull() + private set +var is_20_22_or_greater : Boolean by Delegates.notNull() + private set +var is_20_26_or_greater : Boolean by Delegates.notNull() + private set +var is_20_28_or_greater : Boolean by Delegates.notNull() + private set +var is_20_30_or_greater : Boolean by Delegates.notNull() + private set +var is_20_31_or_greater : Boolean by Delegates.notNull() + private set +var is_20_34_or_greater : Boolean by Delegates.notNull() + private set +var is_20_37_or_greater : Boolean by Delegates.notNull() + private set +var is_20_39_or_greater : Boolean by Delegates.notNull() + private set +var is_20_41_or_greater : Boolean by Delegates.notNull() + private set +var is_20_45_or_greater : Boolean by Delegates.notNull() + private set +var is_20_46_or_greater : Boolean by Delegates.notNull() + private set val versionCheckPatch = resourcePatch( description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.", @@ -84,9 +107,6 @@ val versionCheckPatch = resourcePatch( val playStoreServicesVersion = findPlayStoreServicesVersion() // All bug fix releases always seem to use the same play store version as the minor version. - is_19_03_or_greater = 240402000 <= playStoreServicesVersion - is_19_04_or_greater = 240502000 <= playStoreServicesVersion - is_19_16_or_greater = 241702000 <= playStoreServicesVersion is_19_17_or_greater = 241802000 <= playStoreServicesVersion is_19_18_or_greater = 241902000 <= playStoreServicesVersion is_19_23_or_greater = 242402000 <= playStoreServicesVersion @@ -111,5 +131,19 @@ val versionCheckPatch = resourcePatch( is_20_10_or_greater = 251105000 <= playStoreServicesVersion is_20_14_or_greater = 251505000 <= playStoreServicesVersion is_20_15_or_greater = 251605000 <= playStoreServicesVersion + is_20_19_or_greater = 252005000 <= playStoreServicesVersion + is_20_20_or_greater = 252105000 <= playStoreServicesVersion + is_20_21_or_greater = 252205000 <= playStoreServicesVersion + is_20_22_or_greater = 252305000 <= playStoreServicesVersion + is_20_26_or_greater = 252705000 <= playStoreServicesVersion + is_20_28_or_greater = 252905000 <= playStoreServicesVersion + is_20_30_or_greater = 253105000 <= playStoreServicesVersion + is_20_31_or_greater = 253205000 <= playStoreServicesVersion + is_20_34_or_greater = 253505000 <= playStoreServicesVersion + is_20_37_or_greater = 253805000 <= playStoreServicesVersion + is_20_39_or_greater = 253980000 <= playStoreServicesVersion + is_20_41_or_greater = 254205000 <= playStoreServicesVersion + is_20_45_or_greater = 254605000 <= playStoreServicesVersion + is_20_46_or_greater = 254705000 <= playStoreServicesVersion } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt deleted file mode 100644 index 937365c6ae..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.youtube.misc.privacy - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Patch was renamed", ReplaceWith("sanitizeSharingLinksPatch")) -@Suppress("unused") -val removeTrackingQueryParameterPatch = bytecodePatch{ - dependsOn(sanitizeSharingLinksPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/SanitizeSharingLinksPatch.kt index c39444db04..c3cf0e93d0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/SanitizeSharingLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/SanitizeSharingLinksPatch.kt @@ -12,12 +12,13 @@ val sanitizeSharingLinksPatch = sanitizeSharingLinksPatch( sharedExtensionPatch, settingsPatch, ) + compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) }, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt index 09aa7bf4cd..fb2df5e382 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt @@ -6,7 +6,6 @@ import com.android.tools.smali.dexlib2.Opcode internal val recyclerViewTreeObserverFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CHECK_CAST, Opcode.NEW_INSTANCE, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt index 58033a223e..10a4120d09 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt @@ -12,7 +12,7 @@ val recyclerViewTreeHookPatch = bytecodePatch { execute { recyclerViewTreeObserverFingerprint.method.apply { - val insertIndex = recyclerViewTreeObserverFingerprint.patternMatch!!.startIndex + 1 + val insertIndex = recyclerViewTreeObserverFingerprint.instructionMatches.first().index + 1 val recyclerViewParameter = 2 addRecyclerViewTreeHook = { classDescriptor -> diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt index 3dd3816b52..31e3c87a8f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt @@ -1,15 +1,20 @@ package app.revanced.patches.youtube.misc.settings +import app.revanced.patcher.InstructionLocation.MatchAfterWithin import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.opcode +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode internal val licenseActivityOnCreateFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") - parameters("L") + parameters("Landroid/os/Bundle;") custom { method, classDef -> - classDef.endsWith("LicenseActivity;") && method.name == "onCreate" + method.name == "onCreate" && classDef.endsWith("/LicenseActivity;") } } @@ -17,13 +22,27 @@ internal val setThemeFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters() - literal { appearanceStringId } + instructions( + resourceLiteral(ResourceType.STRING, "app_theme_appearance_dark"), + ) } -internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L - internal val cairoFragmentConfigFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Z") - literal { CAIRO_CONFIG_LITERAL_VALUE } + instructions( + literal(45532100L), + opcode(Opcode.MOVE_RESULT, location = MatchAfterWithin(10)) + ) +} + +// Flag is present in 20.23, but bold icons are missing and forcing them crashes the app. +// 20.31 is the first target with all the bold icons present. +internal val boldIconsFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + instructions( + literal(45685201L) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt index baf4d18ea2..f59533a42f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt @@ -10,19 +10,33 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.overrideThemeColors -import app.revanced.patches.shared.misc.settings.preference.* +import app.revanced.patches.shared.misc.settings.preference.BasePreference +import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.IntentPreference +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.shared.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.fix.contentprovider.fixContentProviderPatch import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_31_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch -import app.revanced.util.* +import app.revanced.util.ResourceGroup +import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.copyResources +import app.revanced.util.findElementByAttributeValueOrThrow +import app.revanced.util.findInstructionIndicesReversedOrThrow +import app.revanced.util.insertLiteralOverride import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation @@ -36,21 +50,19 @@ private const val BASE_ACTIVITY_HOOK_CLASS_DESCRIPTOR = private const val YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/settings/YouTubeActivityHook;" -internal var appearanceStringId = -1L - private set - private val preferences = mutableSetOf() private val settingsResourcePatch = resourcePatch { dependsOn( resourceMappingPatch, settingsPatch( - listOf( + rootPreferences = listOf( IntentPreference( titleKey = "revanced_settings_title", summaryKey = null, - intent = newIntent("revanced_settings_intent"), + intent = newIntent("revanced_settings_intent") ) to "settings_fragment", + PreferenceCategory( titleKey = "revanced_settings_title", layout = "@layout/preference_group_title", @@ -58,40 +70,53 @@ private val settingsResourcePatch = resourcePatch { IntentPreference( titleKey = "revanced_settings_submenu_title", summaryKey = null, - icon = "@drawable/revanced_settings_icon", + icon = "@drawable/revanced_settings_icon_dynamic", layout = "@layout/preference_with_icon", - intent = newIntent("revanced_settings_intent"), + intent = newIntent("revanced_settings_intent") ) ) - ) to "settings_fragment_cairo", + ) to "settings_fragment_cairo" ), - preferences + preferences = preferences ) ) execute { - appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"] - // Use same colors as stock YouTube. overrideThemeColors("@color/yt_white1", "@color/yt_black3") copyResources( "settings", ResourceGroup("drawable", + "revanced_settings_icon_dynamic.xml", "revanced_settings_icon.xml", + "revanced_settings_icon_bold.xml", "revanced_settings_screen_00_about.xml", + "revanced_settings_screen_00_about_bold.xml", "revanced_settings_screen_01_ads.xml", + "revanced_settings_screen_01_ads_bold.xml", "revanced_settings_screen_02_alt_thumbnails.xml", + "revanced_settings_screen_02_alt_thumbnails_bold.xml", "revanced_settings_screen_03_feed.xml", + "revanced_settings_screen_03_feed_bold.xml", "revanced_settings_screen_04_general.xml", + "revanced_settings_screen_04_general_bold.xml", "revanced_settings_screen_05_player.xml", + "revanced_settings_screen_05_player_bold.xml", "revanced_settings_screen_06_shorts.xml", + "revanced_settings_screen_06_shorts_bold.xml", "revanced_settings_screen_07_seekbar.xml", + "revanced_settings_screen_07_seekbar_bold.xml", "revanced_settings_screen_08_swipe_controls.xml", + "revanced_settings_screen_08_swipe_controls_bold.xml", "revanced_settings_screen_09_return_youtube_dislike.xml", + "revanced_settings_screen_09_return_youtube_dislike_bold.xml", "revanced_settings_screen_10_sponsorblock.xml", + "revanced_settings_screen_10_sponsorblock_bold.xml", "revanced_settings_screen_11_misc.xml", + "revanced_settings_screen_11_misc_bold.xml", "revanced_settings_screen_12_video.xml", + "revanced_settings_screen_12_video_bold.xml", ) ) @@ -153,6 +178,7 @@ val settingsPatch = bytecodePatch( addResourcesPatch, versionCheckPatch, fixPlaybackSpeedWhilePlayingPatch, + fixContentProviderPatch, // Currently there is no easy way to make a mandatory patch, // so for now this is a dependent of this patch. checkEnvironmentPatch, @@ -165,6 +191,7 @@ val settingsPatch = bytecodePatch( preferences += NonInteractivePreference( key = "revanced_settings_screen_00_about", icon = "@drawable/revanced_settings_screen_00_about", + iconBold = "@drawable/revanced_settings_screen_00_about_bold", layout = "@layout/preference_with_icon", summaryKey = null, tag = "app.revanced.extension.shared.settings.preference.ReVancedAboutPreference", @@ -179,7 +206,23 @@ val settingsPatch = bytecodePatch( PreferenceScreen.GENERAL_LAYOUT.addPreferences( SwitchPreference("revanced_settings_search_history"), - SwitchPreference("revanced_show_menu_icons") + ) + + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + if (is_20_31_or_greater) { + PreferenceCategory( + titleKey = null, + sorting = Sorting.UNSORTED, + tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory", + preferences = setOf( + SwitchPreference("revanced_show_menu_icons"), + SwitchPreference("revanced_settings_disable_bold_icons") + ) + ) + } else { + SwitchPreference("revanced_show_menu_icons") + } ) PreferenceScreen.MISC.addPreferences( @@ -211,10 +254,21 @@ val settingsPatch = bytecodePatch( // Add setting to force Cairo settings fragment on/off. cairoFragmentConfigFingerprint.method.insertLiteralOverride( - CAIRO_CONFIG_LITERAL_VALUE, + cairoFragmentConfigFingerprint.instructionMatches.first().index, "$YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR->useCairoSettingsFragment(Z)Z" ) + // Bold icon resources are found starting in 20.23, but many YT icons are not bold. + // 20.31 is the first version that seems to have all the bold icons. + if (is_20_31_or_greater) { + boldIconsFeatureFlagFingerprint.let { + it.method.insertLiteralOverride( + it.instructionMatches.first().index, + "$YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR->useBoldIcons(Z)Z" + ) + } + } + modifyActivityForSettingsInjection( licenseActivityOnCreateFingerprint.classDef, licenseActivityOnCreateFingerprint.method, @@ -241,8 +295,9 @@ internal fun modifyActivityForSettingsInjection( // Must modify an existing activity and cannot add a new activity to the manifest, // as that fails for root installations. activityOnCreateMethod.addInstructions( - 1, + 0, """ + invoke-super { p0, p1 }, ${activityOnCreateClass.superclass}->onCreate(Landroid/os/Bundle;)V invoke-static { p0 }, $extensionClassType->initialize(Landroid/app/Activity;)V return-void """ @@ -321,12 +376,14 @@ object PreferenceScreen : BasePreferenceScreen() { key = "revanced_settings_screen_01_ads", summaryKey = null, icon = "@drawable/revanced_settings_screen_01_ads", + iconBold = "@drawable/revanced_settings_screen_01_ads_bold", layout = "@layout/preference_with_icon", ) val ALTERNATIVE_THUMBNAILS = Screen( key = "revanced_settings_screen_02_alt_thumbnails", summaryKey = null, icon = "@drawable/revanced_settings_screen_02_alt_thumbnails", + iconBold = "@drawable/revanced_settings_screen_02_alt_thumbnails_bold", layout = "@layout/preference_with_icon", sorting = Sorting.UNSORTED, ) @@ -334,36 +391,42 @@ object PreferenceScreen : BasePreferenceScreen() { key = "revanced_settings_screen_03_feed", summaryKey = null, icon = "@drawable/revanced_settings_screen_03_feed", + iconBold = "@drawable/revanced_settings_screen_03_feed_bold", layout = "@layout/preference_with_icon", ) val GENERAL_LAYOUT = Screen( key = "revanced_settings_screen_04_general", summaryKey = null, icon = "@drawable/revanced_settings_screen_04_general", + iconBold = "@drawable/revanced_settings_screen_04_general_bold", layout = "@layout/preference_with_icon", ) val PLAYER = Screen( key = "revanced_settings_screen_05_player", summaryKey = null, icon = "@drawable/revanced_settings_screen_05_player", + iconBold = "@drawable/revanced_settings_screen_05_player_bold", layout = "@layout/preference_with_icon", ) val SHORTS = Screen( key = "revanced_settings_screen_06_shorts", summaryKey = null, icon = "@drawable/revanced_settings_screen_06_shorts", + iconBold = "@drawable/revanced_settings_screen_06_shorts_bold", layout = "@layout/preference_with_icon", ) val SEEKBAR = Screen( key = "revanced_settings_screen_07_seekbar", summaryKey = null, icon = "@drawable/revanced_settings_screen_07_seekbar", + iconBold = "@drawable/revanced_settings_screen_07_seekbar_bold", layout = "@layout/preference_with_icon", ) val SWIPE_CONTROLS = Screen( key = "revanced_settings_screen_08_swipe_controls", summaryKey = null, icon = "@drawable/revanced_settings_screen_08_swipe_controls", + iconBold = "@drawable/revanced_settings_screen_08_swipe_controls_bold", layout = "@layout/preference_with_icon", sorting = Sorting.UNSORTED, ) @@ -371,6 +434,7 @@ object PreferenceScreen : BasePreferenceScreen() { key = "revanced_settings_screen_09_return_youtube_dislike", summaryKey = null, icon = "@drawable/revanced_settings_screen_09_return_youtube_dislike", + iconBold = "@drawable/revanced_settings_screen_09_return_youtube_dislike_bold", layout = "@layout/preference_with_icon", sorting = Sorting.UNSORTED, ) @@ -378,6 +442,7 @@ object PreferenceScreen : BasePreferenceScreen() { key = "revanced_settings_screen_10_sponsorblock", summaryKey = null, icon = "@drawable/revanced_settings_screen_10_sponsorblock", + iconBold = "@drawable/revanced_settings_screen_10_sponsorblock_bold", layout = "@layout/preference_with_icon", sorting = Sorting.UNSORTED, ) @@ -385,12 +450,14 @@ object PreferenceScreen : BasePreferenceScreen() { key = "revanced_settings_screen_11_misc", summaryKey = null, icon = "@drawable/revanced_settings_screen_11_misc", + iconBold = "@drawable/revanced_settings_screen_11_misc_bold", layout = "@layout/preference_with_icon", ) val VIDEO = Screen( key = "revanced_settings_screen_12_video", summaryKey = null, icon = "@drawable/revanced_settings_screen_12_video", + iconBold = "@drawable/revanced_settings_screen_12_video_bold", layout = "@layout/preference_with_icon", sorting = Sorting.BY_KEY, ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt index 28cf55bd4b..5928434068 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt @@ -32,10 +32,10 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( block = { compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt deleted file mode 100644 index 44cde6002b..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.youtube.misc.zoomhaptics - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.misc.hapticfeedback.disableHapticFeedbackPatch - -@Deprecated("Superseded by disableHapticFeedbackPatch", ReplaceWith("disableHapticFeedbackPatch")) -val zoomHapticsPatch = bytecodePatch( - description = "Adds an option to disable haptics when zooming.", -) { - dependsOn(disableHapticFeedbackPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt index 44c212e6f8..16eb8630f4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt @@ -1,6 +1,15 @@ package app.revanced.patches.youtube.shared +import app.revanced.patcher.InstructionLocation.MatchAfterImmediately +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.newInstance +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -9,7 +18,7 @@ internal const val YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE = "Lcom/google/android/apps/ internal val conversionContextFingerprintToString = fingerprint { parameters() strings( - "ConversionContext{containerInternal=", + "ConversionContext{", // Partial string match. ", widthConstraint=", ", heightConstraint=", ", templateLoggerFactory=", @@ -21,32 +30,15 @@ internal val conversionContextFingerprintToString = fingerprint { } } -/** - * Resolves to class found in [loopVideoParentFingerprint]. - */ -internal val loopVideoFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - custom { method, _ -> - method.implementation!!.instructions.count() == 3 && method.annotations.isEmpty() - } -} - -internal val loopVideoParentFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - strings( - "play() called when the player wasn't loaded.", - "play() blocked because Background Playability failed", - ) -} - internal val layoutConstructorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") - parameters() - strings("1.0x") + instructions( + literal(159962), + resourceLiteral(ResourceType.ID, "player_control_previous_button_touch_area"), + resourceLiteral(ResourceType.ID, "player_control_next_button_touch_area"), + methodCall(parameters = listOf("Landroid/view/View;", "I")) + ) } internal val mainActivityConstructorFingerprint = fingerprint { @@ -102,10 +94,19 @@ internal val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint { internal val seekbarFingerprint = fingerprint { returns("V") - strings("timed_markers_width") + instructions( + string("timed_markers_width"), + ) } +/** + * Matches to _mutable_ class found in [seekbarFingerprint]. + */ internal val seekbarOnDrawFingerprint = fingerprint { + instructions( + methodCall(smali = "Ljava/lang/Math;->round(F)I"), + opcode(Opcode.MOVE_RESULT, location = MatchAfterImmediately()) + ) custom { method, _ -> method.name == "onDraw" } } @@ -113,16 +114,9 @@ internal val subtitleButtonControllerFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("Lcom/google/android/libraries/youtube/player/subtitles/model/SubtitleTrack;") - opcodes( - Opcode.IGET_OBJECT, - Opcode.IF_NEZ, - Opcode.RETURN_VOID, - Opcode.IGET_BOOLEAN, - Opcode.CONST_4, - Opcode.IF_NEZ, - Opcode.CONST, - Opcode.INVOKE_VIRTUAL, - Opcode.IGET_OBJECT, + instructions( + resourceLiteral(ResourceType.STRING, "accessibility_captions_unavailable"), + resourceLiteral(ResourceType.STRING, "accessibility_captions_button_name"), ) } @@ -130,24 +124,10 @@ internal val videoQualityChangedFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters("L") - opcodes( - Opcode.IGET, // Video resolution (human readable). - Opcode.IGET_OBJECT, - Opcode.IGET_BOOLEAN, - Opcode.IGET_OBJECT, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_DIRECT, - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.GOTO, - Opcode.CONST_4, - Opcode.IF_NE, - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IGET, + instructions( + newInstance("Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;"), + opcode(Opcode.IGET_OBJECT), + opcode(Opcode.CHECK_CAST), + fieldAccess(type = "I", opcode = Opcode.IGET, location = MatchAfterImmediately()), // Video resolution (human readable). ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatch.kt index ea1fed89b1..cbccffe1cb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatch.kt @@ -19,10 +19,10 @@ val forceOriginalAudioPatch = forceOriginalAudioPatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) }, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/DisableVideoCodecsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/DisableVideoCodecsPatch.kt index c5790d1482..a06df5713f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/DisableVideoCodecsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/DisableVideoCodecsPatch.kt @@ -57,10 +57,10 @@ val disableVideoCodecsPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/hdr/DisableHdrPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/hdr/DisableHdrPatch.kt deleted file mode 100644 index d0591f2c7e..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/hdr/DisableHdrPatch.kt +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.patches.youtube.video.hdr - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.video.codecs.disableVideoCodecsPatch - -@Deprecated("Patch was renamed", ReplaceWith("disableVideoCodecsPatch")) -@Suppress("unused") -val disableHdrPatch = bytecodePatch{ - dependsOn(disableVideoCodecsPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt index 74b0e0864b..43e1c75f6e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt @@ -1,6 +1,11 @@ package app.revanced.patches.youtube.video.information +import app.revanced.patcher.InstructionLocation.MatchAfterWithin +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.string import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint import app.revanced.util.getReference import com.android.tools.smali.dexlib2.AccessFlags @@ -9,7 +14,9 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference internal val createVideoPlayerSeekbarFingerprint = fingerprint { returns("V") - strings("timed_markers_width") + instructions( + string("timed_markers_width"), + ) } internal val onPlaybackSpeedItemClickFingerprint = fingerprint { @@ -31,14 +38,18 @@ internal val playerControllerSetTimeReferenceFingerprint = fingerprint { } internal val playerInitFingerprint = fingerprint { - strings("playVideo called on player response with no videoStreamingData.") + instructions( + string("playVideo called on player response with no videoStreamingData."), + ) } /** * Matched using class found in [playerInitFingerprint]. */ internal val seekFingerprint = fingerprint { - strings("Attempting to seek during an ad") + instructions( + string("Attempting to seek during an ad"), + ) } internal val videoLengthFingerprint = fingerprint { @@ -79,7 +90,9 @@ internal val mdxSeekFingerprint = fingerprint { } internal val mdxPlayerDirectorSetVideoStageFingerprint = fingerprint { - strings("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state ") + instructions( + string("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state "), + ) } /** @@ -90,7 +103,6 @@ internal val mdxSeekRelativeFingerprint = fingerprint { // Return type is boolean up to 19.39, and void with 19.39+. parameters("J", "L") opcodes( - Opcode.IGET_OBJECT, Opcode.INVOKE_INTERFACE, ) @@ -109,6 +121,20 @@ internal val seekRelativeFingerprint = fingerprint { ) } +internal val videoEndFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters("J", "L") + instructions( + methodCall( + parameters = listOf(), + returnType = "V" + ), + literal(45368273L, location = MatchAfterWithin(5)), + string("Attempting to seek when video is not playing"), + ) +} + /** * Resolves with the class found in [videoQualityChangedFingerprint]. */ @@ -116,11 +142,8 @@ internal val playbackSpeedMenuSpeedChangedFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("L") parameters("L") - opcodes( - Opcode.IGET, - Opcode.INVOKE_VIRTUAL, - Opcode.SGET_OBJECT, - Opcode.RETURN_OBJECT, + instructions( + fieldAccess(opcode = Opcode.IGET, type = "F") ) } @@ -137,10 +160,27 @@ internal val playbackSpeedClassFingerprint = fingerprint { internal const val YOUTUBE_VIDEO_QUALITY_CLASS_TYPE = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;" +/** + * YouTube 20.19 and lower. + */ +internal val videoQualityLegacyFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters( + "I", // Resolution. + "Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc + "Z", + "L" + ) + custom { _, classDef -> + classDef.type == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE + } +} + internal val videoQualityFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) parameters( "I", // Resolution. + "L", "Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc "Z", "L" diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt index 368aff634d..b09a2dccbc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt @@ -9,6 +9,9 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.smali.toInstructions import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_20_19_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_20_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint import app.revanced.patches.youtube.video.playerresponse.Hook import app.revanced.patches.youtube.video.playerresponse.addPlayerResponseMethodHook @@ -67,6 +70,8 @@ private var speedSelectionValueRegister = -1 private lateinit var setPlaybackSpeedMethod: MutableMethod private var setPlaybackSpeedMethodIndex = -1 +internal lateinit var videoEndMethod: MutableMethod + // Used by other patches. internal lateinit var setPlaybackSpeedContainerClassFieldReference: FieldReference private set @@ -82,6 +87,7 @@ val videoInformationPatch = bytecodePatch( sharedExtensionPatch, videoIdPatch, playerResponseMethodHookPatch, + versionCheckPatch, ) execute { @@ -127,18 +133,22 @@ val videoInformationPatch = bytecodePatch( val videoLengthMethodMatch = videoLengthFingerprint.match(originalClassDef) videoLengthMethodMatch.method.apply { - val videoLengthRegisterIndex = videoLengthMethodMatch.patternMatch!!.endIndex - 2 + val videoLengthRegisterIndex = videoLengthMethodMatch.instructionMatches.last().index - 2 val videoLengthRegister = getInstruction(videoLengthRegisterIndex).registerA val dummyRegisterForLong = videoLengthRegister + 1 // required for long values since they are wide addInstruction( - videoLengthMethodMatch.patternMatch!!.endIndex, + videoLengthMethodMatch.instructionMatches.last().index, "invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, " + "$EXTENSION_CLASS_DESCRIPTOR->setVideoLength(J)V", ) } } + videoEndFingerprint.let { + videoEndMethod = navigate(it.originalMethod).to(it.instructionMatches[0].index).stop() + } + /* * Inject call for video ids */ @@ -161,7 +171,7 @@ val videoInformationPatch = bytecodePatch( * Set the video time method */ timeMethod = navigate(playerControllerSetTimeReferenceFingerprint.originalMethod) - .to(playerControllerSetTimeReferenceFingerprint.patternMatch!!.startIndex) + .to(playerControllerSetTimeReferenceFingerprint.instructionMatches.first().index) .stop() /* @@ -188,8 +198,8 @@ val videoInformationPatch = bytecodePatch( getInstruction(indexOfFirstInstructionOrThrow(Opcode.IF_EQZ) - 1).reference as FieldReference setPlaybackSpeedMethod = - proxy(classes.first { it.type == setPlaybackSpeedMethodReference.definingClass }) - .mutableClass.methods.first { it.name == setPlaybackSpeedMethodReference.name } + mutableClassBy(setPlaybackSpeedMethodReference.definingClass) + .methods.first { it.name == setPlaybackSpeedMethodReference.name } setPlaybackSpeedMethodIndex = 0 // Add override playback speed method. @@ -262,20 +272,23 @@ val videoInformationPatch = bytecodePatch( // Handle new playback speed menu. playbackSpeedMenuSpeedChangedFingerprint.match( videoQualityChangedFingerprint.originalClassDef, - ).method.apply { - val index = indexOfFirstInstructionOrThrow(Opcode.IGET) + ).let { + it.method.apply { + val index = it.instructionMatches.first().index - speedSelectionInsertMethod = this - speedSelectionInsertIndex = index + 1 - speedSelectionValueRegister = getInstruction(index).registerA + speedSelectionInsertMethod = this + speedSelectionInsertIndex = index + 1 + speedSelectionValueRegister = getInstruction(index).registerA + } } - videoQualityFingerprint.let { + (if (is_20_19_or_greater) videoQualityFingerprint else videoQualityLegacyFingerprint).let { // Fix bad data used by YouTube. + val nameRegister = if (is_20_20_or_greater) "p3" else "p2" it.method.addInstructions( 0, """ - invoke-static { p2, p1 }, $EXTENSION_CLASS_DESCRIPTOR->fixVideoQualityResolution(Ljava/lang/String;I)I + invoke-static { $nameRegister, p1 }, $EXTENSION_CLASS_DESCRIPTOR->fixVideoQualityResolution(Ljava/lang/String;I)I move-result p1 """ ) @@ -345,11 +358,7 @@ val videoInformationPatch = bytecodePatch( val setQualityFieldReference = match.method .getInstruction(1).reference as FieldReference - proxy( - classes.find { classDef -> - classDef.type == setQualityFieldReference.type - }!! - ).mutableClass.apply { + mutableClassBy(setQualityFieldReference.type).apply { // Add interface and helper methods to allow extension code to call obfuscated methods. interfaces.add(EXTENSION_VIDEO_QUALITY_MENU_INTERFACE) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt index d958285b4d..725cc48b2c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt @@ -1,11 +1,11 @@ package app.revanced.patches.youtube.video.playerresponse import app.revanced.patcher.fingerprint +import app.revanced.patcher.string import com.android.tools.smali.dexlib2.AccessFlags -import org.stringtemplate.v4.compiler.Bytecode.instructions /** - * For targets 20.15 and later. + * For targets 20.46 and later. */ internal val playerParameterBuilderFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) @@ -25,9 +25,66 @@ internal val playerParameterBuilderFingerprint = fingerprint { "L", "Z", // Appears to indicate if the video id is being opened or is currently playing. "Z", - "Z" + "Z", + "Lj\$/time/Duration;" + ) +} + +/** + * For targets 20.26 and later. + */ +internal val playerParameterBuilder2026Fingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters( + "Ljava/lang/String;", // VideoId. + "[B", + "Ljava/lang/String;", // Player parameters proto buffer. + "Ljava/lang/String;", + "I", + "Z", + "I", + "L", + "Ljava/util/Set;", + "Ljava/lang/String;", + "Ljava/lang/String;", + "L", + "Z", // Appears to indicate if the video id is being opened or is currently playing. + "Z", + "Z", + "Lj\$/time/Duration;" + ) + instructions( + string("psps") + ) +} + +/** + * For targets 20.15 to 20.25 + */ +internal val playerParameterBuilder2015Fingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters( + "Ljava/lang/String;", // VideoId. + "[B", + "Ljava/lang/String;", // Player parameters proto buffer. + "Ljava/lang/String;", + "I", + "Z", + "I", + "L", + "Ljava/util/Set;", + "Ljava/lang/String;", + "Ljava/lang/String;", + "L", + "Z", // Appears to indicate if the video id is being opened or is currently playing. + "Z", + "Z", + ) + instructions( + string("psps") ) - strings("psps") } /** @@ -54,7 +111,9 @@ internal val playerParameterBuilder2010Fingerprint = fingerprint { "Z", "Z" ) - strings("psps") + instructions( + string("psps") + ) } /** @@ -80,7 +139,9 @@ internal val playerParameterBuilder2002Fingerprint = fingerprint { "Z", "Z", ) - strings("psps") + instructions( + string("psps"), + ) } /** @@ -105,11 +166,13 @@ internal val playerParameterBuilder1925Fingerprint = fingerprint { "Z", "Z", ) - strings("psps") + instructions( + string("psps") + ) } /** - * For targets 19.24 and earlier. + * For targets 19.01 to 19.24. */ internal val playerParameterBuilderLegacyFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt index 898f400968..4776add6cc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt @@ -10,6 +10,8 @@ import app.revanced.patches.youtube.misc.playservice.is_19_23_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_02_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_15_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_26_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_46_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch private val hooks = mutableSetOf() @@ -40,9 +42,15 @@ val playerResponseMethodHookPatch = bytecodePatch { execute { val fingerprint : Fingerprint - if (is_20_15_or_greater) { + if (is_20_46_or_greater) { parameterIsShortAndOpeningOrPlaying = 13 fingerprint = playerParameterBuilderFingerprint + } else if (is_20_26_or_greater) { + parameterIsShortAndOpeningOrPlaying = 13 + fingerprint = playerParameterBuilder2026Fingerprint + } else if (is_20_15_or_greater) { + parameterIsShortAndOpeningOrPlaying = 13 + fingerprint = playerParameterBuilder2015Fingerprint } else if (is_20_10_or_greater) { parameterIsShortAndOpeningOrPlaying = 13 fingerprint = playerParameterBuilder2010Fingerprint diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt index 3e8aa23a17..ef7ad9022e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt @@ -5,12 +5,11 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter @@ -25,23 +24,6 @@ internal var videoQualityBottomSheetListFragmentTitle = -1L internal var videoQualityQuickMenuAdvancedMenuDescription = -1L private set -private val advancedVideoQualityMenuResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - - execute { - // Used for the old type of the video quality menu. - videoQualityBottomSheetListFragmentTitle = resourceMappings[ - "layout", - "video_quality_bottom_sheet_list_fragment_title", - ] - - videoQualityQuickMenuAdvancedMenuDescription = resourceMappings[ - "string", - "video_quality_quick_menu_advanced_menu_description", - ] - } -} - private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/playback/quality/AdvancedVideoQualityMenuPatch;" @@ -50,12 +32,12 @@ private const val FILTER_CLASS_DESCRIPTOR = internal val advancedVideoQualityMenuPatch = bytecodePatch { dependsOn( - advancedVideoQualityMenuResourcePatch, sharedExtensionPatch, settingsPatch, addResourcesPatch, lithoFilterPatch, recyclerViewTreeHookPatch, + resourceMappingPatch ) execute { @@ -65,12 +47,23 @@ internal val advancedVideoQualityMenuPatch = bytecodePatch { SwitchPreference("revanced_advanced_video_quality_menu") ) + // Used for the old type of the video quality menu. + videoQualityBottomSheetListFragmentTitle = getResourceId( + ResourceType.LAYOUT, + "video_quality_bottom_sheet_list_fragment_title", + ) + + videoQualityQuickMenuAdvancedMenuDescription = getResourceId( + ResourceType.STRING, + "video_quality_quick_menu_advanced_menu_description", + ) + // region Patch for the old type of the video quality menu. // Used for regular videos when spoofing to old app version, // and for the Shorts quality flyout on newer app versions. videoQualityMenuViewInflateFingerprint.let { it.method.apply { - val checkCastIndex = it.patternMatch!!.endIndex + val checkCastIndex = it.instructionMatches.last().index val listViewRegister = getInstruction(checkCastIndex).registerA addInstruction( @@ -83,9 +76,9 @@ internal val advancedVideoQualityMenuPatch = bytecodePatch { // Force YT to add the 'advanced' quality menu for Shorts. videoQualityMenuOptionsFingerprint.let { - val patternMatch = it.patternMatch!! - val startIndex = patternMatch.startIndex - val insertIndex = patternMatch.endIndex + val patternMatch = it.instructionMatches + val startIndex = patternMatch.first().index + val insertIndex = patternMatch.last().index if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex") it.method.apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt index 31c0c15d5f..f968e3667d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt @@ -1,13 +1,16 @@ package app.revanced.patches.youtube.video.quality import app.revanced.patcher.fingerprint +import app.revanced.patcher.string import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode internal val videoQualityItemOnClickParentFingerprint = fingerprint { returns("V") - strings("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT") + instructions( + string("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT"), + ) } /** diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt index 2f67487c87..45034652fb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt @@ -9,6 +9,8 @@ import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.misc.playservice.is_20_20_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint import app.revanced.patches.youtube.video.information.onCreateHook @@ -25,6 +27,7 @@ val rememberVideoQualityPatch = bytecodePatch { playerTypeHookPatch, settingsPatch, addResourcesPatch, + versionCheckPatch, ) execute { @@ -68,16 +71,15 @@ val rememberVideoQualityPatch = bytecodePatch { ) // Inject a call to remember the user selected quality for regular videos. - videoQualityChangedFingerprint.let { - it.method.apply { - val index = it.patternMatch!!.startIndex - val register = getInstruction(index).registerA + videoQualityChangedFingerprint.method.apply { + val index = videoQualityChangedFingerprint.instructionMatches[3].index + val register = getInstruction(index).registerA - addInstruction( - index + 1, - "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V", - ) - } + addInstruction( + index + 1, + "invoke-static { v$register }, " + + "$EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V", + ) } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityDialogButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityDialogButtonPatch.kt index d2618bb63f..0473f29db6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityDialogButtonPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityDialogButtonPatch.kt @@ -13,7 +13,7 @@ import app.revanced.util.ResourceGroup import app.revanced.util.copyResources private val videoQualityButtonResourcePatch = resourcePatch { - dependsOn(playerControlsResourcePatch) + dependsOn(playerControlsPatch) execute { copyResources( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityPatch.kt index b188238e20..e1e58e4828 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/VideoQualityPatch.kt @@ -24,10 +24,10 @@ val videoQualityPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt index 2e50ba2f6a..2a19db7ca6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt @@ -28,10 +28,10 @@ val playbackSpeedPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt index 4885aa0fe0..2d752eb6d8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt @@ -17,7 +17,7 @@ import app.revanced.util.ResourceGroup import app.revanced.util.copyResources private val playbackSpeedButtonResourcePatch = resourcePatch { - dependsOn(playerControlsResourcePatch) + dependsOn(playerControlsPatch) execute { copyResources( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt index b463755719..b3bc0a593f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt @@ -7,53 +7,36 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.InputType import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.interaction.seekbar.customTapAndHoldFingerprint import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch -import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater +import app.revanced.patches.youtube.misc.playservice.is_19_47_or_greater +import app.revanced.patches.youtube.misc.playservice.is_20_34_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.video.speed.settingsMenuVideoSpeedGroup -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstLiteralInstruction import app.revanced.util.indexOfFirstLiteralInstructionOrThrow +import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.FieldReference -import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.ImmutableField private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilter;" -internal const val EXTENSION_CLASS_DESCRIPTOR = +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch;" -internal var speedUnavailableId = -1L - private set - -private val customPlaybackSpeedResourcePatch = resourcePatch { - dependsOn(resourceMappingPatch) - - execute { - speedUnavailableId = resourceMappings["string", "varispeed_unavailable_message"] - } -} - internal val customPlaybackSpeedPatch = bytecodePatch( description = "Adds custom playback speed options.", ) { @@ -64,7 +47,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch( lithoFilterPatch, versionCheckPatch, recyclerViewTreeHookPatch, - customPlaybackSpeedResourcePatch + resourceMappingPatch ) execute { @@ -77,24 +60,21 @@ internal val customPlaybackSpeedPatch = bytecodePatch( TextPreference( "revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE - ), + ) ) ) - if (is_19_25_or_greater) { + if (is_19_47_or_greater) { settingsMenuVideoSpeedGroup.add( TextPreference("revanced_speed_tap_and_hold", inputType = InputType.NUMBER_DECIMAL), ) } // Override the min/max speeds that can be used. - speedLimiterFingerprint.method.apply { + (if (is_20_34_or_greater) speedLimiterFingerprint else speedLimiterLegacyFingerprint).method.apply { val limitMinIndex = indexOfFirstLiteralInstructionOrThrow(0.25f) - var limitMaxIndex = indexOfFirstLiteralInstruction(2.0f) - // Newer targets have 4x max speed. - if (limitMaxIndex < 0) { - limitMaxIndex = indexOfFirstLiteralInstructionOrThrow(4.0f) - } + // Older unsupported targets use 2.0f and not 4.0f + val limitMaxIndex = indexOfFirstLiteralInstructionOrThrow(4.0f) val limitMinRegister = getInstruction(limitMinIndex).registerA val limitMaxRegister = getInstruction(limitMaxIndex).registerA @@ -103,42 +83,43 @@ internal val customPlaybackSpeedPatch = bytecodePatch( replaceInstruction(limitMaxIndex, "const/high16 v$limitMaxRegister, 8.0f") } + // Turn off client side flag that use server provided min/max speeds. + if (is_20_34_or_greater) { + serverSideMaxSpeedFeatureFlagFingerprint.method.returnEarly(false) + } - // Replace the speeds float array with custom speeds. - // These speeds are used if the speed menu is immediately opened after a video is opened. - speedArrayGeneratorFingerprint.method.apply { - val sizeCallIndex = indexOfFirstInstructionOrThrow { getReference()?.name == "size" } - val sizeCallResultRegister = getInstruction(sizeCallIndex + 1).registerA - - replaceInstruction(sizeCallIndex + 1, "const/4 v$sizeCallResultRegister, 0x0") + // region Force old video quality menu. - val arrayLengthConstIndex = indexOfFirstLiteralInstructionOrThrow(7) - val arrayLengthConstDestination = getInstruction(arrayLengthConstIndex).registerA - val playbackSpeedsArrayType = "$EXTENSION_CLASS_DESCRIPTOR->customPlaybackSpeeds:[F" + // Replace the speeds float array with custom speeds. + speedArrayGeneratorFingerprint.let { + val matches = it.instructionMatches + it.method.apply { + val playbackSpeedsArrayType = "$EXTENSION_CLASS_DESCRIPTOR->customPlaybackSpeeds:[F" + // Apply changes from last index to first to preserve indexes. + + val originalArrayFetchIndex = matches[5].index + val originalArrayFetchDestination = matches[5].getInstruction().registerA + replaceInstruction( + originalArrayFetchIndex, + "sget-object v$originalArrayFetchDestination, $playbackSpeedsArrayType" + ) - addInstructions( - arrayLengthConstIndex + 1, - """ - sget-object v$arrayLengthConstDestination, $playbackSpeedsArrayType - array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination - """, - ) + val arrayLengthConstDestination = matches[3].getInstruction().registerA + val newArrayIndex = matches[4].index + addInstructions( + newArrayIndex, + """ + sget-object v$arrayLengthConstDestination, $playbackSpeedsArrayType + array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination + """ + ) - val originalArrayFetchIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.type == "[F" && reference.definingClass.endsWith("/PlayerConfigModel;") + val sizeCallIndex = matches[0].index + 1 + val sizeCallResultRegister = getInstruction(sizeCallIndex).registerA + replaceInstruction(sizeCallIndex, "const/4 v$sizeCallResultRegister, 0x0") } - val originalArrayFetchDestination = - getInstruction(originalArrayFetchIndex).registerA - - replaceInstruction( - originalArrayFetchIndex, - "sget-object v$originalArrayFetchDestination, $playbackSpeedsArrayType", - ) } - // region Force old video quality menu. - // Add a static INSTANCE field to the class. // This is later used to call "showOldPlaybackSpeedMenu" on the instance. @@ -179,28 +160,30 @@ internal val customPlaybackSpeedPatch = bytecodePatch( // endregion - // Close the unpatched playback dialog and show the modern custom dialog. + // Close the unpatched playback dialog and show the custom speeds. addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR) // Required to check if the playback speed menu is currently shown. addLithoFilter(FILTER_CLASS_DESCRIPTOR) + // endregion + // region Custom tap and hold 2x speed. - if (is_19_25_or_greater) { - disableFastForwardNoticeFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow { - (this as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits() + if (is_19_47_or_greater) { + customTapAndHoldFingerprint.let { + it.method.apply { + val index = it.instructionMatches.first().index + val register = getInstruction(index).registerA + + addInstructions( + index + 1, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->tapAndHoldSpeed()F + move-result v$register + """ + ) } - val register = getInstruction(index).registerA - - addInstructions( - index + 1, - """ - invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->tapAndHoldSpeed()F - move-result v$register - """ - ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt index f39a4136f1..cbdd2730b5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt @@ -1,12 +1,17 @@ package app.revanced.patches.youtube.video.speed.custom +import app.revanced.patcher.fieldAccess import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.newInstance +import app.revanced.patcher.opcode +import app.revanced.patcher.string +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.resourceLiteral import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.StringReference + internal val getOldPlaybackSpeedsFingerprint = fingerprint { parameters("[L", "I") @@ -14,23 +19,54 @@ internal val getOldPlaybackSpeedsFingerprint = fingerprint { } internal val showOldPlaybackSpeedMenuFingerprint = fingerprint { - literal { speedUnavailableId } + instructions( + resourceLiteral(ResourceType.STRING, "varispeed_unavailable_message") + ) } internal val showOldPlaybackSpeedMenuExtensionFingerprint = fingerprint { - custom { method, classDef -> - method.name == "showOldPlaybackSpeedMenu" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + custom { method, _ -> method.name == "showOldPlaybackSpeedMenu" } +} + +internal val serverSideMaxSpeedFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + instructions( + literal(45719140L) + ) } internal val speedArrayGeneratorFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("[L") parameters("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;") - strings("0.0#") + instructions( + methodCall(name = "size", returnType = "I"), + newInstance("Ljava/text/DecimalFormat;"), + string("0.0#"), + literal(7), + opcode(Opcode.NEW_ARRAY), + fieldAccess(definingClass = "/PlayerConfigModel;", type = "[F") + ) } +/** + * 20.34+ + */ internal val speedLimiterFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("F", "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;") + instructions( + literal(0.25f), + literal(4.0f) + ) +} + +/** + * 20.33 and lower. + */ +internal val speedLimiterLegacyFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("F") @@ -45,16 +81,3 @@ internal val speedLimiterFingerprint = fingerprint { Opcode.INVOKE_STATIC, ) } - -internal val disableFastForwardNoticeFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - custom { method, _ -> - method.name == "run" && method.indexOfFirstInstruction { - // In later targets the code is found in different methods with different strings. - val string = getReference()?.string - string == "Failed to easy seek haptics vibrate." || string == "search_landing_cache_key" - } >= 0 - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt index 3924588b42..7925ba2229 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt @@ -1,8 +1,11 @@ package app.revanced.patches.youtube.video.speed.remember import app.revanced.patcher.fingerprint +import app.revanced.patcher.string internal val initializePlaybackSpeedValuesFingerprint = fingerprint { parameters("[L", "I") - strings("menu_item_playback_speed") + instructions( + string("menu_item_playback_speed"), + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt index 6d4a3c6cb8..8f5a505b0e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt @@ -1,7 +1,9 @@ package app.revanced.patches.youtube.video.videoid import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.literal +import app.revanced.patcher.methodCall +import app.revanced.patcher.opcode import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -9,47 +11,43 @@ internal val videoIdFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("L") - opcodes( - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, + instructions( + methodCall( + definingClass = "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;", + returnType = "Ljava/lang/String;" + ), + opcode(Opcode.MOVE_RESULT_OBJECT), ) - custom { method, _ -> - method.indexOfPlayerResponseModelString() >= 0 - } } internal val videoIdBackgroundPlayFingerprint = fingerprint { accessFlags(AccessFlags.DECLARED_SYNCHRONIZED, AccessFlags.FINAL, AccessFlags.PUBLIC) returns("V") parameters("L") - opcodes( - Opcode.IF_EQZ, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IPUT_OBJECT, - Opcode.MONITOR_EXIT, - Opcode.RETURN_VOID, - Opcode.MONITOR_EXIT, - Opcode.RETURN_VOID + instructions( + methodCall( + definingClass = "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;", + returnType = "Ljava/lang/String;" + ), + opcode(Opcode.MOVE_RESULT_OBJECT), + opcode(Opcode.IPUT_OBJECT), + opcode(Opcode.MONITOR_EXIT), + opcode(Opcode.RETURN_VOID), + opcode(Opcode.MONITOR_EXIT), + opcode(Opcode.RETURN_VOID) ) - // The target snippet of code is buried in a huge switch block and the target method - // has been changed many times by YT which makes identifying it more difficult than usual. custom { method, classDef -> - // Access flags changed in 19.36 - AccessFlags.FINAL.isSet(method.accessFlags) && - AccessFlags.DECLARED_SYNCHRONIZED.isSet(method.accessFlags) && - classDef.methods.count() == 17 && - method.implementation != null && - method.indexOfPlayerResponseModelString() >= 0 + method.implementation != null && + (classDef.methods.count() == 17 // 20.39 and lower. + || classDef.methods.count() == 16) // 20.40+ } - } internal val videoIdParentFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("[L") parameters("L") - literal { 524288L } + instructions( + literal(524288L) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt index 6d69381cd3..7ffdb3b2e0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt @@ -9,11 +9,7 @@ import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.video.playerresponse.Hook import app.revanced.patches.youtube.video.playerresponse.addPlayerResponseMethodHook import app.revanced.patches.youtube.video.playerresponse.playerResponseMethodHookPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference /** * Hooks the new video id when the video changes. @@ -96,24 +92,22 @@ val videoIdPatch = bytecodePatch( ) execute { - videoIdFingerprint.match(videoIdParentFingerprint.originalClassDef).method.apply { - videoIdMethod = this - val index = indexOfPlayerResponseModelString() - videoIdRegister = getInstruction(index + 1).registerA - videoIdInsertIndex = index + 2 + videoIdFingerprint.match(videoIdParentFingerprint.originalClassDef).let { + it.method.apply { + videoIdMethod = this + val index = it.instructionMatches.first().index + videoIdRegister = getInstruction(index + 1).registerA + videoIdInsertIndex = index + 2 + } } - videoIdBackgroundPlayFingerprint.method.apply { - backgroundPlaybackMethod = this - val index = indexOfPlayerResponseModelString() - backgroundPlaybackVideoIdRegister = getInstruction(index + 1).registerA - backgroundPlaybackInsertIndex = index + 2 + videoIdBackgroundPlayFingerprint.let { + it.method.apply { + backgroundPlaybackMethod = this + val index = it.instructionMatches.first().index + backgroundPlaybackVideoIdRegister = getInstruction(index + 1).registerA + backgroundPlaybackInsertIndex = index + 2 + } } } -} - -internal fun Method.indexOfPlayerResponseModelString() = indexOfFirstInstruction { - val reference = getReference() - reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" && - reference.returnType == "Ljava/lang/String;" -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt deleted file mode 100644 index 537a2b68c4..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.patches.youtube.video.videoqualitymenu - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.video.quality.videoQualityPatch - -@Suppress("unused") -@Deprecated("Use 'Video Quality' instead.") -val restoreOldVideoQualityMenuPatch = bytecodePatch { - dependsOn(videoQualityPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/Fingerprints.kt deleted file mode 100644 index af38f28f4d..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/Fingerprints.kt +++ /dev/null @@ -1,20 +0,0 @@ -package app.revanced.patches.yuka.misc.unlockpremium - -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint - -internal val isPremiumFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - opcodes( - Opcode.IGET_BOOLEAN, - Opcode.RETURN, - ) -} - -internal val yukaUserConstructorFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - strings("premiumProvider") -} diff --git a/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt deleted file mode 100644 index c4cedd0927..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt +++ /dev/null @@ -1,23 +0,0 @@ -package app.revanced.patches.yuka.misc.unlockpremium - -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("This patch no longer works and will be removed in the future.") -@Suppress("unused") -val unlockPremiumPatch = bytecodePatch { - - compatibleWith("io.yuka.android"("4.29")) - - execute { - isPremiumFingerprint.match( - yukaUserConstructorFingerprint.originalClassDef, - ).method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) - } -} diff --git a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index 993fa820b0..73b96693d7 100644 --- a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -14,9 +14,9 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableField import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.shared.misc.mapping.getResourceId import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.util.InstructionUtils.Companion.branchOpcodes import app.revanced.util.InstructionUtils.Companion.returnOpcodes import app.revanced.util.InstructionUtils.Companion.writeOpcodes @@ -97,7 +97,8 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In return bestFreeRegisterFound } // This method is simple and does not follow branching. - throw IllegalArgumentException("Encountered a branch statement before a free register could be found") + throw IllegalArgumentException("Encountered a branch statement before " + + "a free register could be found from startIndex: $startIndex") } if (instruction.isReturnInstruction) { @@ -359,8 +360,7 @@ fun MutableMethod.addInstructionsAtControlFlowLabel( * @see [indexOfFirstResourceIdOrThrow], [indexOfFirstLiteralInstructionReversed] */ fun Method.indexOfFirstResourceId(resourceName: String): Int { - val resourceId = resourceMappings["id", resourceName] - return indexOfFirstLiteralInstruction(resourceId) + return indexOfFirstLiteralInstruction(getResourceId(ResourceType.ID, resourceName)) } /** @@ -544,7 +544,7 @@ fun BytecodePatchContext.traverseClassHierarchy(targetClass: MutableClass, callb targetClass.superclass ?: return - classBy { targetClass.superclass == it.type }?.mutableClass?.let { + mutableClassByOrNull(targetClass.superclass!!)?.let { traverseClassHierarchy(it, callback) } } @@ -756,8 +756,12 @@ fun Method.findInstructionIndicesReversedOrThrow(opcode: Opcode): List { * Suitable for calls to extension code to override boolean and integer values. */ internal fun MutableMethod.insertLiteralOverride(literal: Long, extensionMethodDescriptor: String) { - // TODO: make this work with objects and wide values. val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) + insertLiteralOverride(literalIndex, extensionMethodDescriptor) +} + +internal fun MutableMethod.insertLiteralOverride(literalIndex: Int, extensionMethodDescriptor: String) { + // TODO: make this work with objects and wide primitive values. val index = indexOfFirstInstructionOrThrow(literalIndex, MOVE_RESULT) val register = getInstruction(index).registerA @@ -781,6 +785,13 @@ internal fun MutableMethod.insertLiteralOverride(literal: Long, extensionMethodD */ internal fun MutableMethod.insertLiteralOverride(literal: Long, override: Boolean) { val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) + return insertLiteralOverride(literalIndex, override) +} + +/** + * Constant value override of the first MOVE_RESULT after the index parameter. + */ +internal fun MutableMethod.insertLiteralOverride(literalIndex: Int, override: Boolean) { val index = indexOfFirstInstructionOrThrow(literalIndex, MOVE_RESULT) val register = getInstruction(index).registerA val overrideValue = if (override) "0x1" else "0x0" @@ -813,21 +824,7 @@ fun BytecodePatchContext.forEachLiteralValueInstruction( } if (matchingIndexes.isNotEmpty()) { - val mutableMethod = proxy(classDef).mutableClass.findMutableMethodOf(method) - - // FIXME: Until patcher V22 is merged, this workaround is needed - // because if multiple patches modify the same class - // then after modifying the method indexes of immutable classes - // are no longer correct. - matchingIndexes.clear() - mutableMethod.instructions.forEachIndexed { index, instruction -> - if ((instruction as? WideLiteralInstruction)?.wideLiteral == literal) { - matchingIndexes.add(index) - } - } - if (matchingIndexes.isEmpty()) return@forEach - // FIXME Remove code above after V22 merge. - + val mutableMethod = mutableClassBy(classDef).findMutableMethodOf(method) matchingIndexes.asReversed().forEach { index -> block.invoke(mutableMethod, index) } @@ -841,17 +838,29 @@ fun BytecodePatchContext.forEachLiteralValueInstruction( private const val RETURN_TYPE_MISMATCH = "Mismatch between override type and Method return type" /** - * Overrides the first instruction of a method with a constant `Boolean` return value. + * Overrides the first instruction of a method with a return-void instruction. * None of the method code will ever execute. * + * @see returnLate + */ +fun MutableMethod.returnEarly() { + check(returnType.first() == 'V') { + RETURN_TYPE_MISMATCH + } + overrideReturnValue(false.toHexString(), false) +} + +/** + * Overrides the first instruction of a method with a constant `Boolean` return value. + * None of the original method code will execute. + * * For methods that return an object or any array type, calling this method with `false` * will force the method to return a `null` value. * * @see returnLate */ -fun MutableMethod.returnEarly(value: Boolean = false) { - val returnType = returnType.first() - check(returnType == 'Z' || (!value && (returnType == 'V' || returnType == 'L' || returnType != '['))) { +fun MutableMethod.returnEarly(value: Boolean) { + check(returnType.first() == 'Z') { RETURN_TYPE_MISMATCH } overrideReturnValue(value.toHexString(), false) @@ -859,7 +868,7 @@ fun MutableMethod.returnEarly(value: Boolean = false) { /** * Overrides the first instruction of a method with a constant `Byte` return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * @see returnLate */ @@ -870,7 +879,7 @@ fun MutableMethod.returnEarly(value: Byte) { /** * Overrides the first instruction of a method with a constant `Short` return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * @see returnLate */ @@ -881,7 +890,7 @@ fun MutableMethod.returnEarly(value: Short) { /** * Overrides the first instruction of a method with a constant `Char` return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * @see returnLate */ @@ -892,7 +901,7 @@ fun MutableMethod.returnEarly(value: Char) { /** * Overrides the first instruction of a method with a constant `Int` return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * @see returnLate */ @@ -903,7 +912,7 @@ fun MutableMethod.returnEarly(value: Int) { /** * Overrides the first instruction of a method with a constant `Long` return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * @see returnLate */ @@ -914,7 +923,7 @@ fun MutableMethod.returnEarly(value: Long) { /** * Overrides the first instruction of a method with a constant `Float` return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * @see returnLate */ @@ -925,7 +934,7 @@ fun MutableMethod.returnEarly(value: Float) { /** * Overrides the first instruction of a method with a constant `Double` return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * @see returnLate */ @@ -936,7 +945,7 @@ fun MutableMethod.returnEarly(value: Double) { /** * Overrides the first instruction of a method with a constant String return value. - * None of the method code will ever execute. + * None of the original method code will execute. * * Target method must have return type * Ljava/lang/String; or Ljava/lang/CharSequence; @@ -950,6 +959,21 @@ fun MutableMethod.returnEarly(value: String) { overrideReturnValue(value, false) } +/** + * Overrides the first instruction of a method with a constant `NULL` return value. + * None of the original method code will execute. + * + * @param value Value must be `Null`. + * @see returnLate + */ +fun MutableMethod.returnEarly(value: Void?) { + val returnType = returnType.first() + check(returnType == 'L' || returnType != '[') { + RETURN_TYPE_MISMATCH + } + overrideReturnValue(false.toHexString(), false) +} + /** * Overrides all return statements with a constant `Boolean` value. * All method code is executed the same as unpatched. @@ -960,11 +984,7 @@ fun MutableMethod.returnEarly(value: String) { * @see returnEarly */ fun MutableMethod.returnLate(value: Boolean) { - val returnType = returnType.first() - if (returnType == 'V') { - error("Cannot return late for Method of void type") - } - check(returnType == 'Z' || (!value && (returnType == 'L' || returnType == '['))) { + check(this.returnType.first() == 'Z') { RETURN_TYPE_MISMATCH } @@ -1064,6 +1084,22 @@ fun MutableMethod.returnLate(value: String) { overrideReturnValue(value, true) } +/** + * Overrides all return statements with a constant `Null` value. + * All method code is executed the same as unpatched. + * + * @param value Value must be `Null`. + * @see returnEarly + */ +fun MutableMethod.returnLate(value: Void?) { + val returnType = returnType.first() + check(returnType == 'L' || returnType == '[') { + RETURN_TYPE_MISMATCH + } + + overrideReturnValue(false.toHexString(), true) +} + private fun MutableMethod.overrideReturnValue(value: String, returnLate: Boolean) { val instructions = if (returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;" ) { """ @@ -1140,10 +1176,7 @@ internal fun BytecodePatchContext.addStaticFieldToExtension( objectClass: String, smaliInstructions: String ) { - val classDef = classes.find { classDef -> classDef.type == className } - ?: throw PatchException("No matching methods found in: $className") - val mutableClass = proxy(classDef).mutableClass - + val mutableClass = mutableClassBy(className) val objectCall = "$mutableClass->$fieldName:$objectClass" mutableClass.apply { @@ -1175,7 +1208,7 @@ internal fun BytecodePatchContext.addStaticFieldToExtension( * * @param literalSupplier The supplier for the literal value to check for. */ -// TODO: add a way for subclasses to also use their own custom fingerprint. +@Deprecated("Instead use instruction filters and `literal()`") fun FingerprintBuilder.literal(literalSupplier: () -> Long) { custom { method, _ -> method.containsLiteralInstruction(literalSupplier()) diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index 5ff3d86934..097a7d9412 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -375,13 +375,32 @@ @string/revanced_miniplayer_type_entry_0 @string/revanced_miniplayer_type_entry_1 @string/revanced_miniplayer_type_entry_2 - @string/revanced_miniplayer_type_entry_3 + @string/revanced_miniplayer_type_entry_4 @string/revanced_miniplayer_type_entry_5 @string/revanced_miniplayer_type_entry_6 @string/revanced_miniplayer_type_entry_7 + DISABLED + DEFAULT + MINIMAL + MODERN_1 + MODERN_2 + MODERN_3 + MODERN_4 + + + @string/revanced_miniplayer_type_entry_0 + @string/revanced_miniplayer_type_entry_1 + @string/revanced_miniplayer_type_entry_2 + @string/revanced_miniplayer_type_entry_3 + @string/revanced_miniplayer_type_entry_4 + @string/revanced_miniplayer_type_entry_5 + @string/revanced_miniplayer_type_entry_6 + @string/revanced_miniplayer_type_entry_7 + + DISABLED DEFAULT MINIMAL @@ -567,14 +586,6 @@ - - @string/revanced_shorts_player_type_shorts - @string/revanced_shorts_player_type_regular_player - - - SHORTS_PLAYER - REGULAR_PLAYER - @string/revanced_shorts_player_type_shorts @string/revanced_shorts_player_type_regular_player diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 38c3bfb9db..e633737a55 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -82,6 +82,9 @@ Second \"item\" text" Show settings search history Settings search history is shown Settings search history is not shown + Disable bold icons + Icons are not bold + Icons are bold Show ReVanced setting icons Setting icons are shown Setting icons are not shown @@ -750,6 +753,9 @@ If changing this setting does not take effect, try switching to Incognito mode." Hide navigation button labels Labels are hidden Labels are shown + Enable navigation bar animations + Navigation transitions are animated + Navigation transitions are not animated Disable translucent status bar Status bar is opaque Status bar is opaque or translucent @@ -869,6 +875,9 @@ To show the Audio track menu, change \'Spoof video streams\' to \'Android No SDK Hide video thumbnails seekbar Video thumbnails seekbar is hidden Video thumbnails seekbar is shown + Enable fullscreen large seekbar + Fullscreen seekbar is large size + Fullscreen seekbar is normal size Shorts player diff --git a/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml index a243eea714..a3f7a9e119 100644 --- a/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml +++ b/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml @@ -11,7 +11,7 @@ style="@style/YouTubePlayerButton" android:layout_width="48.0dip" android:layout_height="60.0dip" - android:paddingTop="6.5dp" + android:paddingTop="6.0dp" android:paddingBottom="0dp" android:longClickable="false" android:scaleType="center" diff --git a/patches/src/main/resources/loopvideobutton/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/loopvideobutton/host/layout/youtube_controls_bottom_ui_container.xml index bcd7def9e7..1947f997e0 100644 --- a/patches/src/main/resources/loopvideobutton/host/layout/youtube_controls_bottom_ui_container.xml +++ b/patches/src/main/resources/loopvideobutton/host/layout/youtube_controls_bottom_ui_container.xml @@ -1,22 +1,21 @@ - + - + android:id="@+id/revanced_loop_video_button" + style="@style/YouTubePlayerButton" + android:layout_width="48.0dip" + android:layout_height="60.0dip" + android:longClickable="false" + android:paddingTop="6.0dp" + android:paddingBottom="0dp" + android:scaleType="center" + android:src="@drawable/revanced_loop_video_button_off" + yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" + yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" /> diff --git a/patches/src/main/resources/navigationbuttons/drawable/revanced_fill_bell_cairo_black_24.xml b/patches/src/main/resources/navigationbuttons/drawable/revanced_fill_bell_cairo_black_24.xml new file mode 100644 index 0000000000..f69f9b329a --- /dev/null +++ b/patches/src/main/resources/navigationbuttons/drawable/revanced_fill_bell_cairo_black_24.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_ic_dialog_alert.xml b/patches/src/main/resources/settings/drawable/revanced_ic_dialog_alert.xml index d308d6f8ca..3fd2696b3a 100644 --- a/patches/src/main/resources/settings/drawable/revanced_ic_dialog_alert.xml +++ b/patches/src/main/resources/settings/drawable/revanced_ic_dialog_alert.xml @@ -1,10 +1,10 @@ + android:width="40dp" + android:height="40dp" + android:viewportWidth="40" + android:viewportHeight="40"> + android:fillColor="#000000" + android:fillType="evenOdd" + android:pathData="M22.1641,5.24609 L36.5547,30.168 C37.5156,31.8359,36.3164,33.918,34.3906,33.918 L5.60938,33.918 C3.68359,33.918,2.48438,31.8359,3.44531,30.168 L17.8359,5.24609 C18.7969,3.58203,21.2031,3.58203,22.1641,5.24609 Z M20,25 C19.0781,25,18.332,25.7461,18.332,26.668 C18.332,27.5898,19.0781,28.332,20,28.332 C20.9219,28.332,21.668,27.5898,21.668,26.668 C21.668,25.7461,20.9219,25,20,25 Z M20,13.332 C19.1445,13.332,18.4414,13.9766,18.3438,14.8047 L18.332,15 L18.332,21.668 C18.332,22.5898,19.0781,23.332,20,23.332 C20.8555,23.332,21.5586,22.6875,21.6563,21.8633 L21.668,21.668 L21.668,15 C21.668,14.0781,20.9219,13.332,20,13.332 Z M20,13.332" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_arrow_time.xml b/patches/src/main/resources/settings/drawable/revanced_settings_arrow_time.xml index 6c43a8a43a..a0b9a55c32 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_arrow_time.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_arrow_time.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M14.97,16.95L10,13.87V7h2v5.76l4.03,2.49L14.97,16.95zM22,12c0,5.51 -4.49,10 -10,10S2,17.51 2,12h1c0,4.96 4.04,9 9,9s9,-4.04 9,-9s-4.04,-9 -9,-9C8.81,3 5.92,4.64 4.28,7.38C4.17,7.56 4.06,7.75 3.97,7.94C3.96,7.96 3.95,7.98 3.94,8H8v1H1.96V3h1v4.74C3,7.65 3.03,7.57 3.07,7.49C3.18,7.27 3.3,7.07 3.42,6.86C5.22,3.86 8.51,2 12,2C17.51,2 22,6.49 22,12z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_arrow_time_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_arrow_time_bold.xml new file mode 100644 index 0000000000..1666db5b22 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_arrow_time_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_custom_checkmark.xml b/patches/src/main/resources/settings/drawable/revanced_settings_custom_checkmark.xml index 0623e325f7..b7895fdd00 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_custom_checkmark.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_custom_checkmark.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M9.29446,19 L3.4,12.708 L4.24339,11.8029 L9.29446,17.1896 L20.1565,5.6 L21,6.50026 Z M9.29446,19" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_custom_checkmark_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_custom_checkmark_bold.xml new file mode 100644 index 0000000000..d12c3282ac --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_custom_checkmark_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_icon_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_icon_bold.xml new file mode 100644 index 0000000000..c929e507de --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_icon_bold.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_icon_dynamic.xml b/patches/src/main/resources/settings/drawable/revanced_settings_icon_dynamic.xml new file mode 100644 index 0000000000..bc1f5584ec --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_icon_dynamic.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_00_about.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_00_about.xml index ddc7120197..eb0e23c75c 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_00_about.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_00_about.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M13,17h-2v-6h2V17zM13,7h-2v2h2V7zM12,3c-4.96,0 -9,4.04 -9,9s4.04,9 9,9c4.96,0 9,-4.04 9,-9S16.96,3 12,3M12,2c5.52,0 10,4.48 10,10s-4.48,10 -10,10C6.48,22 2,17.52 2,12S6.48,2 12,2L12,2z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_00_about_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_00_about_bold.xml new file mode 100644 index 0000000000..b931198cf6 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_00_about_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_01_ads.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_01_ads.xml index 2654631d88..fb26813b95 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_01_ads.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_01_ads.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M17.9219,12.5 L17.9219,11.5 L21.1523,11.5 L21.1523,12.5 Z M19.0078,18.9609 L16.4219,17.0234 L17.0469,16.2305 L19.6289,18.168 Z M16.9688,7.69141 L16.3477,6.89844 L18.9297,4.96094 L19.5547,5.75391 Z M5.5,17.9609 L5.5,14.1523 L4.46094,14.1523 C4.01563,14.1523,3.63281,13.9961,3.31641,13.6836 C3.00391,13.3672,2.84766,12.9844,2.84766,12.5391 L2.84766,11.4609 C2.84766,11.0156,3.00391,10.6328,3.31641,10.3164 C3.63281,10.0039,4.01563,9.84766,4.46094,9.84766 L8.19141,9.84766 L12.1523,7.5 L12.1523,16.5 L8.19141,14.1523 L6.5,14.1523 L6.5,17.9609 Z M11.1523,14.7188 L11.1523,9.28125 L8.47266,10.8477 L4.46094,10.8477 C4.30859,10.8477,4.16797,10.9102,4.03906,11.0391 C3.91016,11.168,3.84766,11.3086,3.84766,11.4609 L3.84766,12.5391 C3.84766,12.6914,3.91016,12.832,4.03906,12.9609 C4.16797,13.0898,4.30859,13.1523,4.46094,13.1523 L8.47266,13.1523 Z M13.9219,14.8867 L13.9219,9.11328 C14.2578,9.42188,14.5273,9.82813,14.7305,10.332 C14.9375,10.8398,15.0391,11.3945,15.0391,12 C15.0391,12.6055,14.9375,13.1602,14.7305,13.668 C14.5273,14.1719,14.2578,14.5781,13.9219,14.8867 Z M7.5,12 Z M7.5,12" /> \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_01_ads_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_01_ads_bold.xml new file mode 100644 index 0000000000..f341714f33 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_01_ads_bold.xml @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_02_alt_thumbnails.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_02_alt_thumbnails.xml index 4b9486c897..7fcbe912b7 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_02_alt_thumbnails.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_02_alt_thumbnails.xml @@ -6,16 +6,16 @@ Copyright 2023 Ajay Ramachandran --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12.0814,1.99794 C9.37251,1.99794,6.97872,3.32778,5.20335,5.14149 C3.42798,6.9552,1.99794,9.39989,1.99794,12.1677 C1.99794,14.9355,3.10403,17.7107,4.87939,19.5244 C6.65476,21.3381,9.37195,21.9549,12.0814,21.9549 C14.7908,21.9549,17.0848,20.9067,18.8601,19.093 C20.6355,17.2793,22.002,14.9355,22.002,12.1677 C22.002,9.40046,20.8525,6.83638,19.0766,5.02267 C17.3013,3.20894,14.7903,1.99794,12.0814,1.99794 Z M11.9105,5.02102 C13.838,5.02102,15.5196,6.09439,16.7829,7.35711 C18.0462,8.61984,18.8878,10.3004,18.8878,12.2279 C18.8878,14.1554,18.2513,16.0427,16.988,17.3054 C15.7247,18.5681,13.8374,18.9333,11.9105,18.9333 C9.98355,18.9333,8.36976,18.2962,7.10645,17.0335 C5.84314,15.7708,5.11222,14.1554,5.11222,12.2278 C5.11222,10.3003,5.63697,8.47868,6.8997,7.21537 C8.16239,5.95218,9.98293,5.02102,11.9105,5.02102 Z" + android:strokeWidth="1" + android:strokeColor="?android:attr/textColorPrimary" /> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M15.3108,11.899 C15.3108,13.6514,13.8411,15.1264,12.0887,15.1264 C10.3363,15.1264,8.97704,13.6515,8.97704,11.899 C8.97704,10.1466,10.3363,8.71961,12.0887,8.71961 C13.8411,8.71961,15.3108,10.1466,15.3108,11.899 Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_02_alt_thumbnails_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_02_alt_thumbnails_bold.xml new file mode 100644 index 0000000000..9854f09fc0 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_02_alt_thumbnails_bold.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_03_feed.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_03_feed.xml index 2af656695a..ce88360a8e 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_03_feed.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_03_feed.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M4.85,19.775Q4.15,19.775 3.688,19.312Q3.225,18.85 3.225,18.15V12.55H4.225V18.15Q4.225,18.375 4.425,18.575Q4.625,18.775 4.85,18.775H12.45V19.775ZM8.625,16Q7.925,16 7.463,15.537Q7,15.075 7,14.375V8.775H8V14.375Q8,14.625 8.188,14.812Q8.375,15 8.625,15H16.225V16ZM12.375,12.225Q11.7,12.225 11.238,11.762Q10.775,11.3 10.775,10.625V5.85Q10.775,5.15 11.238,4.687Q11.7,4.225 12.375,4.225H19.15Q19.85,4.225 20.312,4.687Q20.775,5.15 20.775,5.85V10.625Q20.775,11.3 20.312,11.762Q19.85,12.225 19.15,12.225ZM12.375,11.225H19.15Q19.375,11.225 19.575,11.037Q19.775,10.85 19.775,10.625V7.225H11.775V10.625Q11.775,10.85 11.963,11.037Q12.15,11.225 12.375,11.225Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_03_feed_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_03_feed_bold.xml new file mode 100644 index 0000000000..edce57018e --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_03_feed_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_04_general.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_04_general.xml index c6a2625bbe..8c6d6cd7fb 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_04_general.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_04_general.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M12,9.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5S9.5,13.38 9.5,12S10.62,9.5 12,9.5M12,8.5c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S13.93,8.5 12,8.5L12,8.5zM13.22,3l0.55,2.2l0.13,0.51l0.5,0.18c0.61,0.23 1.19,0.56 1.72,0.98l0.4,0.32l0.5,-0.14l2.17,-0.62l1.22,2.11l-1.63,1.59l-0.37,0.36l0.08,0.51c0.05,0.32 0.08,0.64 0.08,0.98s-0.03,0.66 -0.08,0.98l-0.08,0.51l0.37,0.36l1.63,1.59l-1.22,2.11l-2.17,-0.62l-0.5,-0.14l-0.4,0.32c-0.53,0.43 -1.11,0.76 -1.72,0.98l-0.5,0.18l-0.13,0.51L13.22,21h-2.44l-0.55,-2.2l-0.13,-0.51l-0.5,-0.18C9,17.88 8.42,17.55 7.88,17.12l-0.4,-0.32l-0.5,0.14l-2.17,0.62L3.6,15.44l1.63,-1.59l0.37,-0.36l-0.08,-0.51C5.47,12.66 5.44,12.33 5.44,12s0.03,-0.66 0.08,-0.98l0.08,-0.51l-0.37,-0.36L3.6,8.56l1.22,-2.11l2.17,0.62l0.5,0.14l0.4,-0.32C8.42,6.45 9,6.12 9.61,5.9l0.5,-0.18l0.13,-0.51L10.78,3H13.22M14,2h-4L9.26,4.96c-0.73,0.27 -1.4,0.66 -2,1.14L4.34,5.27l-2,3.46l2.19,2.13C4.47,11.23 4.44,11.61 4.44,12s0.03,0.77 0.09,1.14l-2.19,2.13l2,3.46l2.92,-0.83c0.6,0.48 1.27,0.87 2,1.14L10,22h4l0.74,-2.96c0.73,-0.27 1.4,-0.66 2,-1.14l2.92,0.83l2,-3.46l-2.19,-2.13c0.06,-0.37 0.09,-0.75 0.09,-1.14s-0.03,-0.77 -0.09,-1.14l2.19,-2.13l-2,-3.46L16.74,6.1c-0.6,-0.48 -1.27,-0.87 -2,-1.14L14,2L14,2z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_04_general_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_04_general_bold.xml new file mode 100644 index 0000000000..5a14a69b52 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_04_general_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_05_player.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_05_player.xml index 94c5ce84a0..160c95eb91 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_05_player.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_05_player.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M11.5,20.5 L11.5,15.5 L12.5,15.5 L12.5,17.5 L20.5,17.5 L20.5,18.5 L12.5,18.5 L12.5,20.5 Z M3.5,18.5 L3.5,17.5 L8.5,17.5 L8.5,18.5 Z M7.5,14.5 L7.5,12.5 L3.5,12.5 L3.5,11.5 L7.5,11.5 L7.5,9.5 L8.5,9.5 L8.5,14.5 Z M11.5,12.5 L11.5,11.5 L20.5,11.5 L20.5,12.5 Z M15.5,8.5 L15.5,3.5 L16.5,3.5 L16.5,5.5 L20.5,5.5 L20.5,6.5 L16.5,6.5 L16.5,8.5 Z M3.5,6.5 L3.5,5.5 L12.5,5.5 L12.5,6.5 Z M3.5,6.5" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_05_player_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_05_player_bold.xml new file mode 100644 index 0000000000..fde9e7cea6 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_05_player_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_06_shorts.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_06_shorts.xml index c16728ae2d..091ea60a36 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_06_shorts.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_06_shorts.xml @@ -15,15 +15,15 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M8.54688,22.2031 C7.00781,22.2031,5.51172,21.375,4.73047,19.9219 C3.66797,17.9414,4.32031,15.4531,6.21875,14.25 C6.23047,14.2422,6.24219,14.2344,6.25391,14.2305 C6.40625,14.1523,6.55469,14.0703,6.70703,13.9844 C6.59375,13.9219,6.48438,13.8594,6.37109,13.793 C5.20703,13.1211,4.49219,12.1758,4.29688,11.0508 C4.04297,9.55078,4.04297,7.65625,6.32031,6.28516 C8.07031,5.23828,9.86719,4.21484,11.6016,3.22266 C12.1797,2.89453,12.7539,2.56641,13.332,2.23438 C14.5313,1.54688,16.0195,1.51953,17.3008,2.16016 C18.5977,2.80469,19.4805,4.01563,19.6641,5.40625 C19.9297,7.17578,19.0469,8.94531,17.4609,9.80469 C17.3594,9.86328,17.2578,9.91797,17.1602,9.97656 C17.2539,10.0313,17.3477,10.0859,17.4414,10.1406 C18.7422,10.8711,19.5664,12.1172,19.6953,13.5547 C19.8242,14.9766,19.25,16.332,18.1211,17.2734 C17.7617,17.5664,17.3633,17.793,16.9805,18.0078 C16.8672,18.0703,16.7578,18.1328,16.6484,18.1953 C14.8711,19.2344,12.7617,20.457,10.5859,21.6875 C9.9375,22.0352,9.23828,22.2031,8.54688,22.2031 Z M6.69141,15.0313 C5.20703,15.9805,4.69922,17.9375,5.53125,19.4922 C6.42578,21.1484,8.49609,21.7773,10.1445,20.8906 C12.3086,19.668,14.4141,18.4453,16.1875,17.4102 C16.3008,17.3438,16.418,17.2773,16.5313,17.2148 C16.8984,17.0078,17.2461,16.8125,17.543,16.5703 C18.4336,15.8281,18.8906,14.7578,18.7891,13.6367 C18.6836,12.5039,18.0313,11.5156,16.9922,10.9336 C16.8281,10.8359,16.6641,10.7383,16.4961,10.6367 C16.3516,10.5508,16.2031,10.4609,16.043,10.3711 C15.9063,10.2891,15.8203,10.1445,15.8164,9.98438 C15.8125,9.82422,15.8984,9.67188,16.0352,9.58984 C16.3516,9.39063,16.6641,9.20703,17.0195,9.00781 C18.2773,8.32813,18.9727,6.92969,18.7617,5.53125 C18.6172,4.4375,17.9219,3.48438,16.8984,2.97656 C15.8828,2.47266,14.7188,2.49219,13.7813,3.02344 C13.207,3.35547,12.6289,3.68359,12.0547,4.01172 C10.3242,5.00391,8.53125,6.02344,6.78906,7.06641 C5.34375,7.9375,4.88281,9.04688,5.19531,10.8984 C5.34375,11.7539,5.89063,12.4648,6.82422,13 C7.15234,13.1875,7.47656,13.375,7.83984,13.5898 C7.97656,13.6719,8.0625,13.8203,8.0625,13.9805 C8.0625,14.1406,7.98047,14.2891,7.83984,14.375 C7.44531,14.6133,7.08203,14.8281,6.69141,15.0313 Z M6.69141,15.0313" /> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M10.0703,15.3555 C9.99219,15.3555,9.91406,15.3359,9.84375,15.2969 C9.69922,15.2148,9.61328,15.0625,9.61328,14.9023 L9.61328,9.08594 C9.61328,8.92188,9.69922,8.76953,9.83984,8.69141 C9.98438,8.60938,10.1563,8.60938,10.2969,8.69141 L15.3281,11.5898 C15.4688,11.6719,15.5547,11.8242,15.5547,11.9844 C15.5586,12.1484,15.4688,12.3008,15.3281,12.3789 L10.2969,15.2969 C10.2266,15.3359,10.1484,15.3555,10.0703,15.3555 Z M10.5234,9.875 L10.5234,14.1094 L14.1914,11.9883 Z M10.5234,9.875" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_06_shorts_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_06_shorts_bold.xml new file mode 100644 index 0000000000..2b616d939b --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_06_shorts_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_07_seekbar.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_07_seekbar.xml index 92cc9e881f..d37211fdd4 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_07_seekbar.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_07_seekbar.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M4.5,14Q3.65,14 3.075,13.425Q2.5,12.85 2.5,12Q2.5,11.15 3.075,10.575Q3.65,10 4.5,10Q5.2,10 5.738,10.425Q6.275,10.85 6.425,11.5H21.5V12.5H6.425Q6.275,13.15 5.738,13.575Q5.2,14 4.5,14Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_07_seekbar_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_07_seekbar_bold.xml new file mode 100644 index 0000000000..12fe4e8b79 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_07_seekbar_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_08_swipe_controls.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_08_swipe_controls.xml index 27ddec3fe1..d804354496 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_08_swipe_controls.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_08_swipe_controls.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M3,21 L3,20.1328 L5.3125,20.1328 C4.33203,18.9844,3.54297,17.7266,2.95703,16.3516 C2.37109,14.9805,2.07813,13.5313,2.07813,12.0078 C2.07813,10.4883,2.37109,9.04297,2.95703,7.67578 C3.54297,6.30859,4.33203,5.04688,5.3125,3.88281 L3,3.88281 L3,3 L7.09766,3 L7.09766,7.11719 L6.23047,7.11719 L6.23047,4.17578 C5.25,5.28906,4.45703,6.50391,3.85156,7.82422 C3.24609,9.14063,2.94141,10.5352,2.94141,12.0078 C2.94141,13.4844,3.24609,14.875,3.85156,16.1875 C4.45703,17.4961,5.25,18.7031,6.23047,19.8047 L6.23047,16.9023 L7.09766,16.9023 L7.09766,21 Z M16.2969,19.8047 C15.9883,19.9141,15.668,19.9648,15.3359,19.9531 C15,19.9453,14.6836,19.8711,14.3789,19.7266 L8.0625,16.7891 L8.35938,16.1953 C8.46094,16.0156,8.59375,15.8711,8.75781,15.7656 C8.92188,15.6563,9.10547,15.5938,9.30859,15.5781 L12.1602,15.2578 L9.30469,7.44922 C9.25391,7.3125,9.26172,7.18359,9.32031,7.0625 C9.38281,6.94531,9.48047,6.85938,9.61719,6.80859 C9.75391,6.76172,9.88281,6.76563,10.0039,6.82813 C10.1211,6.88672,10.207,6.98438,10.2539,7.12109 L13.5508,16.1797 L9.80078,16.5078 L14.8086,18.8242 C14.9766,18.8984,15.1602,18.9453,15.3672,18.957 C15.5703,18.9727,15.7617,18.9453,15.9414,18.8711 L19.3867,17.6211 C20.043,17.3867,20.5195,16.957,20.8086,16.332 C21.1016,15.7109,21.1289,15.0703,20.8945,14.4102 L19.5195,10.6602 C19.4688,10.5234,19.4727,10.3945,19.5234,10.2734 C19.5781,10.1523,19.6719,10.0703,19.8125,10.0195 C19.9492,9.97266,20.0781,9.97266,20.1992,10.0273 C20.3203,10.0781,20.4023,10.1758,20.4531,10.3125 L21.8281,14.0625 C22.1719,14.9844,22.1406,15.8789,21.7383,16.75 C21.332,17.6172,20.668,18.2188,19.7422,18.5547 Z M14.5273,13.5469 L13.1563,9.76953 C13.1094,9.63281,13.1133,9.50391,13.1758,9.38281 C13.2344,9.26563,13.332,9.17969,13.4727,9.13281 C13.6094,9.08203,13.7383,9.08594,13.8555,9.14844 C13.9766,9.20703,14.0586,9.30469,14.1094,9.44141 L15.4844,13.1914 Z M17.1992,12.5586 L16.1719,9.73438 C16.125,9.59766,16.1289,9.46875,16.1914,9.35547 C16.25,9.23828,16.3477,9.16016,16.4844,9.11328 C16.625,9.0625,16.7539,9.06641,16.8711,9.12109 C16.9922,9.17188,17.0781,9.26563,17.125,9.40625 L18.1484,12.2266 Z M17.0898,15.2578 Z M17.0898,15.2578" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_08_swipe_controls_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_08_swipe_controls_bold.xml new file mode 100644 index 0000000000..6ced78823d --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_08_swipe_controls_bold.xml @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_09_return_youtube_dislike.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_09_return_youtube_dislike.xml index 046daf7d0c..4eea6e9690 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_09_return_youtube_dislike.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_09_return_youtube_dislike.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M3.625,15Q3,15 2.5,14.5Q2,14 2,13.375V12.15Q2,12.025 2.025,11.862Q2.05,11.7 2.1,11.55L4.85,5.075Q5.05,4.625 5.538,4.312Q6.025,4 6.55,4H16.575V15L10.3,21.2L9.875,20.75Q9.725,20.625 9.638,20.4Q9.55,20.175 9.55,20V19.85L10.575,15ZM15.575,5H6.55Q6.325,5 6.1,5.112Q5.875,5.225 5.775,5.5L3,12V13.375Q3,13.65 3.175,13.825Q3.35,14 3.625,14H11.8L10.65,19.45L15.575,14.575ZM15.575,14.575V14Q15.575,14 15.575,13.825Q15.575,13.65 15.575,13.375V12V5.5Q15.575,5.225 15.575,5.112Q15.575,5 15.575,5ZM16.575,15V14H20V5H16.575V4H21V15Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_09_return_youtube_dislike_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_09_return_youtube_dislike_bold.xml new file mode 100644 index 0000000000..d35c3fb7b8 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_09_return_youtube_dislike_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_10_sponsorblock.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_10_sponsorblock.xml index 0ca8266de4..be1f45953c 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_10_sponsorblock.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_10_sponsorblock.xml @@ -6,11 +6,11 @@ Copyright 2021 Ajay Ramachandran --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M 12.000145,2.0000008 C 8.8230689,1.9990926 5.6959192,2.7864027 2.9017488,4.2906678 2.3373945,4.5948398 1.9899198,5.1860103 2.000223,5.8244635 2.0930396,12.358829 5.4926743,18.31271 11.094442,21.749998 c 0.557183,0.333336 1.253849,0.333336 1.811031,0 5.601767,-3.438045 9.001096,-9.391169 9.094295,-15.9255345 0.01052,-0.6386247 -0.337035,-1.2300179 -0.9016,-1.5341683 -2.794107,-1.5040456 -5.92111,-2.2912233 -9.098023,-2.2902944 z m 0.08082,0.8705548 c 3.003625,0.013255 5.957553,0.7636027 8.599879,2.1845129 0.277414,0.151228 0.448533,0.4421907 0.44513,0.7568723 C 21.034684,12.23921 17.58825,17.8544 12.446767,21.009378 c -0.274165,0.167124 -0.619386,0.167124 -0.893551,0 C 6.4117365,17.854399 2.9652339,12.239209 2.8739372,5.8119397 2.8705209,5.4972741 3.0416092,5.2063196 3.3189962,5.0550685 6.0095892,3.608201 9.0224769,2.8570356 12.080969,2.8705556 Z M 9.6351953,6.7701615 v 8.3406435 l 7.2606727,-4.170358 z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_10_sponsorblock_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_10_sponsorblock_bold.xml new file mode 100644 index 0000000000..08e788ef55 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_10_sponsorblock_bold.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_11_misc.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_11_misc.xml index 3cf7dba65d..c2d5483a71 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_11_misc.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_11_misc.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M16.3,11.95 L12.075,7.725 16.3,3.5 20.525,7.725ZM4.625,10.625V4.625H10.625V10.625ZM13.375,19.375V13.375H19.375V19.375ZM4.625,19.375V13.375H10.625V19.375ZM5.625,9.625H9.625V5.625H5.625ZM16.325,10.575 L19.15,7.75 16.325,4.925 13.5,7.75ZM14.375,18.375H18.375V14.375H14.375ZM5.625,18.375H9.625V14.375H5.625ZM9.625,9.625ZM13.5,7.75ZM9.625,14.375ZM14.375,14.375Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_11_misc_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_11_misc_bold.xml new file mode 100644 index 0000000000..ac63c495f8 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_11_misc_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_12_video.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_12_video.xml index 87c0b6d4f3..fe1683f0ba 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_screen_12_video.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_12_video.xml @@ -17,11 +17,11 @@ Copyright 2022 Google --> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M12,19H4.625Q3.925,19 3.463,18.538Q3,18.075 3,17.375V6.625Q3,5.925 3.463,5.463Q3.925,5 4.625,5H19.375Q20.075,5 20.538,5.463Q21,5.925 21,6.625V11H20V6.625Q20,6.35 19.825,6.175Q19.65,6 19.375,6H4.625Q4.35,6 4.175,6.175Q4,6.35 4,6.625V17.375Q4,17.65 4.175,17.825Q4.35,18 4.625,18H12ZM10,15.575V8.425L15.575,12ZM17.85,20.8 L17.75,19.95Q17.175,19.825 16.8,19.6Q16.425,19.375 16.1,19.025L15.3,19.4L14.725,18.525L15.45,17.95Q15.25,17.425 15.25,16.925Q15.25,16.425 15.45,15.9L14.725,15.3L15.3,14.45L16.1,14.8Q16.425,14.475 16.8,14.25Q17.175,14.025 17.75,13.9L17.85,13.05H18.85L18.95,13.9Q19.525,14.025 19.9,14.25Q20.275,14.475 20.6,14.825L21.4,14.45L21.975,15.325L21.25,15.9Q21.45,16.425 21.45,16.925Q21.45,17.425 21.25,17.95L21.975,18.525L21.4,19.4L20.6,19.025Q20.275,19.375 19.9,19.6Q19.525,19.825 18.95,19.95L18.85,20.8ZM18.35,19.075Q19.225,19.075 19.863,18.438Q20.5,17.8 20.5,16.925Q20.5,16.05 19.863,15.413Q19.225,14.775 18.35,14.775Q17.475,14.775 16.837,15.413Q16.2,16.05 16.2,16.925Q16.2,17.8 16.837,18.438Q17.475,19.075 18.35,19.075Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_screen_12_video_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_screen_12_video_bold.xml new file mode 100644 index 0000000000..22a7d6febe --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_screen_12_video_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_search_icon.xml b/patches/src/main/resources/settings/drawable/revanced_settings_search_icon.xml index e34340b6ca..89a59b85b3 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_search_icon.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_search_icon.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M11.0882,3 C6.62072,3,3,6.62368,3,11.0911 C3,15.5615,6.62368,19.1822,11.0911,19.1822 C13.172,19.1822,15.0691,18.3954,16.5029,17.1039 L20.094,20.695 C21.1138,19.6754,19.8953,20.8968,21,19.7921 L17.3822,16.1773 C18.5074,14.7874,19.1822,13.018,19.1822,11.0911 C19.1793,6.62072,15.5556,3,11.0882,3 Z M11.0882,4.27895 C14.851,4.27895,17.9004,7.32829,17.9004,11.0911 C17.9004,14.851,14.8511,17.9003,11.0882,17.9003 C7.32537,17.9003,4.27603,14.851,4.27603,11.0911 C4.27898,7.32827,7.32833,4.28189,11.0882,4.27893 Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_search_icon_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_search_icon_bold.xml new file mode 100644 index 0000000000..22fcf34b9e --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_search_icon_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_search_remove.xml b/patches/src/main/resources/settings/drawable/revanced_settings_search_remove.xml index 721d378856..62a8d1c302 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_search_remove.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_search_remove.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="?android:attr/textColorPrimary" + android:pathData="M7.61719,20 C7.16797,20,6.78516,19.8438,6.47266,19.5273 C6.15625,19.2148,6,18.832,6,18.3828 L6,6 L5,6 L5,5 L9,5 L9,4.23047 L15,4.23047 L15,5 L19,5 L19,6 L18,6 L18,18.3828 C18,18.8438,17.8477,19.2305,17.5391,19.5391 C17.2305,19.8477,16.8438,20,16.3828,20 Z M17,6 L7,6 L7,18.3828 C7,18.5625,7.05859,18.7109,7.17188,18.8281 C7.28906,18.9414,7.4375,19,7.61719,19 L16.3828,19 C16.5391,19,16.6797,18.9375,16.8086,18.8086 C16.9375,18.6797,17,18.5391,17,18.3828 Z M9.80859,17 L10.8086,17 L10.8086,8 L9.80859,8 Z M13.1914,17 L14.1914,17 L14.1914,8 L13.1914,8 Z M7,6 L7,19 Z M7,6" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_search_remove_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_search_remove_bold.xml new file mode 100644 index 0000000000..3cb5d7d788 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_search_remove_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_toolbar_arrow_left.xml b/patches/src/main/resources/settings/drawable/revanced_settings_toolbar_arrow_left.xml index dd8932acfd..3490e8c8c4 100644 --- a/patches/src/main/resources/settings/drawable/revanced_settings_toolbar_arrow_left.xml +++ b/patches/src/main/resources/settings/drawable/revanced_settings_toolbar_arrow_left.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#000000" + android:pathData="M11.5,3.5 L3.5,11.5 L11.5,19.5 L12.3145,18.6914 L5.69531,12.0723 L21,12.0723 L21,10.9277 L5.69531,10.9277 L12.3145,4.30859 Z" /> diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_toolbar_arrow_left_bold.xml b/patches/src/main/resources/settings/drawable/revanced_settings_toolbar_arrow_left_bold.xml new file mode 100644 index 0000000000..f93ab850c0 --- /dev/null +++ b/patches/src/main/resources/settings/drawable/revanced_settings_toolbar_arrow_left_bold.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/settings/xml/revanced_prefs_icons_bold.xml b/patches/src/main/resources/settings/xml/revanced_prefs_icons_bold.xml new file mode 100644 index 0000000000..66304a5c07 --- /dev/null +++ b/patches/src/main/resources/settings/xml/revanced_prefs_icons_bold.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml index 6b55b0da98..ab680d2a59 100644 --- a/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml +++ b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml @@ -1,16 +1,16 @@ - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + android:fillColor="@android:color/white" + android:pathData="M 12.000145,2.0000008 C 8.8230689,1.9990926 5.6959192,2.7864027 2.9017488,4.2906678 2.3373945,4.5948398 1.9899198,5.1860103 2.000223,5.8244635 2.0930396,12.358829 5.4926743,18.31271 11.094442,21.749998 c 0.557183,0.333336 1.253849,0.333336 1.811031,0 5.601767,-3.438045 9.001096,-9.391169 9.094295,-15.9255345 0.01052,-0.6386247 -0.337035,-1.2300179 -0.9016,-1.5341683 -2.794107,-1.5040456 -5.92111,-2.2912233 -9.098023,-2.2902944 z m 0.08082,0.8705548 c 3.003625,0.013255 5.957553,0.7636027 8.599879,2.1845129 0.277414,0.151228 0.448533,0.4421907 0.44513,0.7568723 C 21.034684,12.23921 17.58825,17.8544 12.446767,21.009378 c -0.274165,0.167124 -0.619386,0.167124 -0.893551,0 C 6.4117365,17.854399 2.9652339,12.239209 2.8739372,5.8119397 2.8705209,5.4972741 3.0416092,5.2063196 3.3189962,5.0550685 6.0095892,3.608201 9.0224769,2.8570356 12.080969,2.8705556 Z M 9.6351953,6.7701615 v 8.3406435 l 7.2606727,-4.170358 z" /> diff --git a/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo_bold.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo_bold.xml new file mode 100644 index 0000000000..058128b131 --- /dev/null +++ b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo_bold.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 357fde7cdf..04e2c477d6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ rootProject.name = "revanced-patches" pluginManagement { repositories { + mavenLocal() gradlePluginPortal() google() maven {