diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 2d6d258f47..0000000000 --- a/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -[*.{kt,kts}] -ktlint_code_style = intellij_idea -ktlint_standard_no-wildcard-imports = disabled \ No newline at end of file diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 1202bc5f0d..505feaa0b6 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -34,7 +34,7 @@ jobs: - name: Build env: - ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ env.GITHUB_ACTOR }} + ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }} ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }} run: ./gradlew :patches:buildAndroid --no-daemon 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 android.widget.Toolbar; 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 @@ public class MusicActivityHook extends BaseActivityHook { // 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/build.gradle.kts b/extensions/shared/build.gradle.kts index 8f037894ca..13949a8dc6 100644 --- a/extensions/shared/build.gradle.kts +++ b/extensions/shared/build.gradle.kts @@ -2,3 +2,9 @@ dependencies { implementation(project(":extensions:shared:library")) compileOnly(libs.okhttp) } + +android { + defaultConfig { + minSdk = 26 + } +} 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 ea37cfcde8..0093317082 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.Window; 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.Collator; 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 class Utils { /** * 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 class Utils { */ 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 class Utils { 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 class Utils { * @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,22 +327,18 @@ public class Utils { /** * @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 String getResourceString(int id) throws Resources.NotFoundException { @@ -316,29 +346,29 @@ public class Utils { } 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 { @@ -349,7 +379,7 @@ public class Utils { * 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; } @@ -806,6 +836,21 @@ public class Utils { 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. */ @@ -1167,4 +1212,18 @@ public class Utils { 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 androidx.annotation.Nullable; 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 @@ abstract class Check { // 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.Request; 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 java.util.Locale; 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 @@ public class CustomBrandingPatch { 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/patches/components/CustomFilter.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java similarity index 96% rename from extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/components/CustomFilter.java rename to extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java index bd5388f31f..c82c28353a 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/components/CustomFilter.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java @@ -1,4 +1,4 @@ -package app.revanced.extension.shared.patches.components; +package app.revanced.extension.shared.patches.litho; import static app.revanced.extension.shared.StringRef.str; @@ -17,7 +17,6 @@ import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.ByteTrieSearch; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; import app.revanced.extension.shared.settings.YouTubeAndMusicSettings; -import app.revanced.extension.shared.patches.litho.Filter; /** * Allows custom filtering using a path and optionally a proto buffer string. @@ -148,7 +147,7 @@ public final class CustomFilter extends Filter { @Override public boolean isFiltered(String identifier, String path, byte[] buffer, - StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { + StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { // All callbacks are custom filter groups. CustomFilterGroup custom = (CustomFilterGroup) matchedGroup; if (custom.startsWith && contentIndex != 0) { diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java index 1bab323544..ccd152262b 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java @@ -1,12 +1,12 @@ package app.revanced.extension.shared.patches.litho; +import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; +import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; -import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; - /** * Filters litho based components. * @@ -33,12 +33,12 @@ public abstract class Filter { * Identifier callbacks. Do not add to this instance, * and instead use {@link #addIdentifierCallbacks(StringFilterGroup...)}. */ - protected final List identifierCallbacks = new ArrayList<>(); + public final List identifierCallbacks = new ArrayList<>(); /** * Path callbacks. Do not add to this instance, * and instead use {@link #addPathCallbacks(StringFilterGroup...)}. */ - protected final List pathCallbacks = new ArrayList<>(); + public final List pathCallbacks = new ArrayList<>(); /** * Adds callbacks to {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)} diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroup.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroup.java index 34219de9dc..5b75858f3d 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroup.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroup.java @@ -55,7 +55,7 @@ public abstract class FilterGroup { } protected final BooleanSetting setting; - protected final T[] filters; + public final T[] filters; /** * Initialize a new filter group. diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroupList.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroupList.java index e68276c7ce..da22ca9ff7 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroupList.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/FilterGroupList.java @@ -1,15 +1,17 @@ package app.revanced.extension.shared.patches.litho; import androidx.annotation.NonNull; - -import java.util.*; - import app.revanced.extension.shared.ByteTrieSearch; import app.revanced.extension.shared.StringTrieSearch; import app.revanced.extension.shared.TrieSearch; import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + public abstract class FilterGroupList> implements Iterable { private final List filterGroups = new ArrayList<>(); diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java index 5ff86428db..ddcfe41cc0 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java @@ -4,14 +4,17 @@ import androidx.annotation.NonNull; 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.StringTrieSearch; -import app.revanced.extension.shared.settings.BaseSettings; -import app.revanced.extension.shared.settings.YouTubeAndMusicSettings; - +import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; +import app.revanced.extension.shared.settings.BaseSettings; +import app.revanced.extension.shared.StringTrieSearch; +import app.revanced.extension.shared.settings.YouTubeAndMusicSettings; @SuppressWarnings("unused") public final class LithoFilterPatch { @@ -34,7 +37,7 @@ public final class LithoFilterPatch { public String toString() { // Estimate the percentage of the buffer that are Strings. StringBuilder builder = new StringBuilder(Math.max(100, buffer.length / 2)); - builder.append( "ID: "); + builder.append("ID: "); builder.append(identifier); builder.append(" Path: "); builder.append(path); @@ -75,6 +78,16 @@ public final class LithoFilterPatch { } } + /** + * 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. *

@@ -92,25 +105,55 @@ public final class LithoFilterPatch { private static final int LITHO_LAYOUT_THREAD_POOL_SIZE = 1; /** - * Placeholder for actual filters. + * For YouTube 20.22+, this is set to true by a patch, + * because it cannot use the thread buffer due to the buffer frequently not being 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. + *

+ * This is set during patching, do not change manually. */ - private static final class DummyFilter extends Filter { } + private static final boolean EXTRACT_IDENTIFIER_FROM_BUFFER = false; - 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 byte[] LITHO_COMPONENT_EXTENSION_BYTES = ".e".getBytes(StandardCharsets.US_ASCII); + /** + * Used as placeholder for litho id/path filters that do not use a buffer + */ 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, @@ -162,16 +205,107 @@ public final class LithoFilterPatch { } } + 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); } /** @@ -179,46 +313,70 @@ public final class LithoFilterPatch { * 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/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.TextView; 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 @@ import app.revanced.extension.shared.ui.Dim; 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. @@ -123,16 +124,18 @@ public abstract class BaseActivityHook extends Activity { toolBarParent.addView(toolbar, 0); } + /** + * Returns the resource ID for the content view layout. + */ + protected int getContentViewResourceId() { + return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR; + } + /** * Customizes the activity's theme. */ protected abstract void customizeActivityTheme(Activity activity); - /** - * Returns the resource ID for the content view layout. - */ - protected abstract int getContentViewResourceId(); - /** * 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 f7c6a3215f..c8570d2513 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 java.lang.Boolean.TRUE; 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); + public static final BooleanSetting GMS_CORE_CHECK_UPDATES = new BooleanSetting("revanced_gms_core_check_updates", true, true); // @@ -46,4 +57,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 androidx.annotation.Nullable; 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.Locale; 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 android.widget.TextView; 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 @@ import app.revanced.extension.shared.ui.CustomDialog; @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 class CustomDialogListPreference extends ListPreference { 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.Set; 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 @@ import app.revanced.extension.shared.ui.Dim; 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 android.widget.Toolbar; 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 class ToolbarPreferenceFragment extends AbstractPreferenceFragment { */ @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.List; 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 abstract class BaseSearchResultItem { // 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.lang.reflect.Method; 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 class SearchHistoryManager { // 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 class SearchHistoryManager { public void notifyDataSetChanged() { container.removeAllViews(); for (String query : history) { - View view = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_ITEM, container, false); - - TextView historyText = view.findViewById(ID_HISTORY_TEXT); - ImageView deleteIcon = view.findViewById(ID_DELETE_ICON); - - historyText.setText(query); - + 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)); + // 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 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.ByteBuffer; 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 class StreamingDataRequest { */ 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/build.gradle.kts b/extensions/spotify/build.gradle.kts index fd346d93b5..39d58a0227 100644 --- a/extensions/spotify/build.gradle.kts +++ b/extensions/spotify/build.gradle.kts @@ -1,14 +1,7 @@ -plugins { - alias(libs.plugins.protobuf) -} - dependencies { compileOnly(project(":extensions:shared:library")) compileOnly(project(":extensions:spotify:stub")) compileOnly(libs.annotation) - - implementation(libs.nanohttpd) - implementation(libs.protobuf.javalite) } android { @@ -21,19 +14,3 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } } - -protobuf { - protoc { - artifact = libs.protobuf.protoc.get().toString() - } - - generateProtoTasks { - all().forEach { task -> - task.builtins { - create("java") { - option("lite") - } - } - } - } -} 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/misc/fix/ClientTokenService.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/ClientTokenService.java deleted file mode 100644 index 0345b19ef6..0000000000 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/ClientTokenService.java +++ /dev/null @@ -1,115 +0,0 @@ -package app.revanced.extension.spotify.misc.fix; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import app.revanced.extension.shared.Logger; -import app.revanced.extension.spotify.misc.fix.clienttoken.data.v0.ClienttokenHttp.*; - -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; - -import static app.revanced.extension.spotify.misc.fix.Constants.*; - -class ClientTokenService { - private static final String IOS_CLIENT_ID = "58bd3c95768941ea9eb4350aaa033eb3"; - private static final String IOS_USER_AGENT; - - static { - String clientVersion = getClientVersion(); - int commitHashIndex = clientVersion.lastIndexOf("."); - String version = clientVersion.substring( - clientVersion.indexOf("-") + 1, - clientVersion.lastIndexOf(".", commitHashIndex - 1) - ); - - IOS_USER_AGENT = "Spotify/" + version + " iOS/" + getSystemVersion() + " (" + getHardwareMachine() + ")"; - } - - private static final ConnectivitySdkData.Builder IOS_CONNECTIVITY_SDK_DATA = - ConnectivitySdkData.newBuilder() - .setPlatformSpecificData(PlatformSpecificData.newBuilder() - .setIos(NativeIOSData.newBuilder() - .setHwMachine(getHardwareMachine()) - .setSystemVersion(getSystemVersion()) - ) - ); - - private static final ClientDataRequest.Builder IOS_CLIENT_DATA_REQUEST = - ClientDataRequest.newBuilder() - .setClientVersion(getClientVersion()) - .setClientId(IOS_CLIENT_ID); - - private static final ClientTokenRequest.Builder IOS_CLIENT_TOKEN_REQUEST = - ClientTokenRequest.newBuilder() - .setRequestType(ClientTokenRequestType.REQUEST_CLIENT_DATA_REQUEST); - - - @NonNull - static ClientTokenRequest newIOSClientTokenRequest(String deviceId) { - Logger.printInfo(() -> "Creating new iOS client token request with device ID: " + deviceId); - - return IOS_CLIENT_TOKEN_REQUEST - .setClientData(IOS_CLIENT_DATA_REQUEST - .setConnectivitySdkData(IOS_CONNECTIVITY_SDK_DATA - .setDeviceId(deviceId) - ) - ) - .build(); - } - - @Nullable - static ClientTokenResponse getClientTokenResponse(@NonNull ClientTokenRequest request) { - if (request.getRequestType() == ClientTokenRequestType.REQUEST_CLIENT_DATA_REQUEST) { - Logger.printInfo(() -> "Requesting iOS client token"); - String deviceId = request.getClientData().getConnectivitySdkData().getDeviceId(); - request = newIOSClientTokenRequest(deviceId); - } - - ClientTokenResponse response; - try { - response = requestClientToken(request); - } catch (IOException ex) { - Logger.printException(() -> "Failed to handle request", ex); - return null; - } - - return response; - } - - @NonNull - private static ClientTokenResponse requestClientToken(@NonNull ClientTokenRequest request) throws IOException { - HttpURLConnection urlConnection = (HttpURLConnection) new URL(CLIENT_TOKEN_API_URL).openConnection(); - urlConnection.setRequestMethod("POST"); - urlConnection.setDoOutput(true); - urlConnection.setRequestProperty("Content-Type", "application/x-protobuf"); - urlConnection.setRequestProperty("Accept", "application/x-protobuf"); - urlConnection.setRequestProperty("User-Agent", IOS_USER_AGENT); - - byte[] requestArray = request.toByteArray(); - urlConnection.setFixedLengthStreamingMode(requestArray.length); - urlConnection.getOutputStream().write(requestArray); - - try (InputStream inputStream = urlConnection.getInputStream()) { - return ClientTokenResponse.parseFrom(inputStream); - } - } - - @Nullable - static ClientTokenResponse serveClientTokenRequest(@NonNull InputStream inputStream) { - ClientTokenRequest request; - try { - request = ClientTokenRequest.parseFrom(inputStream); - } catch (IOException ex) { - Logger.printException(() -> "Failed to parse request from input stream", ex); - return null; - } - Logger.printInfo(() -> "Request of type: " + request.getRequestType()); - - ClientTokenResponse response = getClientTokenResponse(request); - if (response != null) Logger.printInfo(() -> "Response of type: " + response.getResponseType()); - - return response; - } -} diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/Constants.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/Constants.java deleted file mode 100644 index 4928da7adc..0000000000 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/Constants.java +++ /dev/null @@ -1,26 +0,0 @@ -package app.revanced.extension.spotify.misc.fix; - -import androidx.annotation.NonNull; - -class Constants { - static final String CLIENT_TOKEN_API_PATH = "/v1/clienttoken"; - static final String CLIENT_TOKEN_API_URL = "https://clienttoken.spotify.com" + CLIENT_TOKEN_API_PATH; - - // Modified by a patch. Do not touch. - @NonNull - static String getClientVersion() { - return ""; - } - - // Modified by a patch. Do not touch. - @NonNull - static String getSystemVersion() { - return ""; - } - - // Modified by a patch. Do not touch. - @NonNull - static String getHardwareMachine() { - return ""; - } -} diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/RequestListener.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/RequestListener.java deleted file mode 100644 index 2de6caa3ee..0000000000 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/RequestListener.java +++ /dev/null @@ -1,94 +0,0 @@ -package app.revanced.extension.spotify.misc.fix; - -import androidx.annotation.NonNull; -import app.revanced.extension.shared.Logger; -import app.revanced.extension.spotify.misc.fix.clienttoken.data.v0.ClienttokenHttp.ClientTokenResponse; -import com.google.protobuf.MessageLite; -import fi.iki.elonen.NanoHTTPD; - -import java.io.ByteArrayInputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Objects; - -import static app.revanced.extension.spotify.misc.fix.ClientTokenService.serveClientTokenRequest; -import static app.revanced.extension.spotify.misc.fix.Constants.CLIENT_TOKEN_API_PATH; -import static fi.iki.elonen.NanoHTTPD.Response.Status.INTERNAL_ERROR; - -class RequestListener extends NanoHTTPD { - RequestListener(int port) { - super(port); - - try { - start(); - } catch (IOException ex) { - Logger.printException(() -> "Failed to start request listener on port " + port, ex); - throw new RuntimeException(ex); - } - } - - @NonNull - @Override - public Response serve(@NonNull IHTTPSession session) { - String uri = session.getUri(); - if (!uri.equals(CLIENT_TOKEN_API_PATH)) return INTERNAL_ERROR_RESPONSE; - - Logger.printInfo(() -> "Serving request for URI: " + uri); - - ClientTokenResponse response = serveClientTokenRequest(getInputStream(session)); - if (response != null) return newResponse(Response.Status.OK, response); - - Logger.printException(() -> "Failed to serve client token request"); - return INTERNAL_ERROR_RESPONSE; - } - - @NonNull - private static InputStream newLimitedInputStream(InputStream inputStream, long contentLength) { - return new FilterInputStream(inputStream) { - private long remaining = contentLength; - - @Override - public int read() throws IOException { - if (remaining <= 0) return -1; - int result = super.read(); - if (result != -1) remaining--; - return result; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (remaining <= 0) return -1; - len = (int) Math.min(len, remaining); - int result = super.read(b, off, len); - if (result != -1) remaining -= result; - return result; - } - }; - } - - @NonNull - private static InputStream getInputStream(@NonNull IHTTPSession session) { - long requestContentLength = Long.parseLong(Objects.requireNonNull(session.getHeaders().get("content-length"))); - return newLimitedInputStream(session.getInputStream(), requestContentLength); - } - - private static final Response INTERNAL_ERROR_RESPONSE = newResponse(INTERNAL_ERROR); - - @SuppressWarnings("SameParameterValue") - @NonNull - private static Response newResponse(Response.Status status) { - return newResponse(status, null); - } - - @NonNull - private static Response newResponse(Response.IStatus status, MessageLite messageLite) { - if (messageLite == null) { - return newFixedLengthResponse(status, "application/x-protobuf", null); - } - - byte[] messageBytes = messageLite.toByteArray(); - InputStream stream = new ByteArrayInputStream(messageBytes); - return newFixedLengthResponse(status, "application/x-protobuf", stream, messageBytes.length); - } -} diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/SpoofClientPatch.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/SpoofClientPatch.java deleted file mode 100644 index fee58b26c0..0000000000 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/SpoofClientPatch.java +++ /dev/null @@ -1,25 +0,0 @@ -package app.revanced.extension.spotify.misc.fix; - -import app.revanced.extension.shared.Logger; - -@SuppressWarnings("unused") -public class SpoofClientPatch { - private static RequestListener listener; - - /** - * Injection point. Launch requests listener server. - */ - public synchronized static void launchListener(int port) { - if (listener != null) { - Logger.printInfo(() -> "Listener already running on port " + port); - return; - } - - try { - Logger.printInfo(() -> "Launching listener on port " + port); - listener = new RequestListener(port); - } catch (Exception ex) { - Logger.printException(() -> "launchListener failure", ex); - } - } -} 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 @@ package app.revanced.extension.spotify.shared; 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 @@ public final class ComponentFilters { 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/spotify/src/main/proto/app/revanced/extension/spotify/misc/fix/clienttoken_http.proto b/extensions/spotify/src/main/proto/app/revanced/extension/spotify/misc/fix/clienttoken_http.proto deleted file mode 100644 index 8e7f242b76..0000000000 --- a/extensions/spotify/src/main/proto/app/revanced/extension/spotify/misc/fix/clienttoken_http.proto +++ /dev/null @@ -1,73 +0,0 @@ -syntax = "proto3"; - -package spotify.clienttoken.data.v0; - -option optimize_for = LITE_RUNTIME; -option java_package = "app.revanced.extension.spotify.misc.fix.clienttoken.data.v0"; - -message ClientTokenRequest { - ClientTokenRequestType request_type = 1; - - oneof request { - ClientDataRequest client_data = 2; - } -} - -enum ClientTokenRequestType { - REQUEST_UNKNOWN = 0; - REQUEST_CLIENT_DATA_REQUEST = 1; - REQUEST_CHALLENGE_ANSWERS_REQUEST = 2; -} - -message ClientDataRequest { - string client_version = 1; - string client_id = 2; - - oneof data { - ConnectivitySdkData connectivity_sdk_data = 3; - } -} - -message ConnectivitySdkData { - PlatformSpecificData platform_specific_data = 1; - string device_id = 2; -} - -message PlatformSpecificData { - oneof data { - NativeIOSData ios = 2; - } -} - -message NativeIOSData { - int32 user_interface_idiom = 1; - bool target_iphone_simulator = 2; - string hw_machine = 3; - string system_version = 4; - string simulator_model_identifier = 5; -} - -message ClientTokenResponse { - ClientTokenResponseType response_type = 1; - - oneof response { - GrantedTokenResponse granted_token = 2; - } -} - -enum ClientTokenResponseType { - RESPONSE_UNKNOWN = 0; - RESPONSE_GRANTED_TOKEN_RESPONSE = 1; - RESPONSE_CHALLENGES_RESPONSE = 2; -} - -message GrantedTokenResponse { - string token = 1; - int32 expires_after_seconds = 2; - int32 refresh_after_seconds = 3; - repeated TokenDomain domains = 4; -} - -message TokenDomain { - string domain = 1; -} diff --git a/extensions/strava/src/main/java/app/revanced/extension/strava/AddMediaDownloadPatch.java b/extensions/strava/src/main/java/app/revanced/extension/strava/AddMediaDownloadPatch.java index e0e3b6814f..1ddb99ffad 100644 --- a/extensions/strava/src/main/java/app/revanced/extension/strava/AddMediaDownloadPatch.java +++ b/extensions/strava/src/main/java/app/revanced/extension/strava/AddMediaDownloadPatch.java @@ -10,6 +10,7 @@ import android.os.Environment; import android.provider.MediaStore; import android.webkit.MimeTypeMap; +import app.revanced.extension.shared.ResourceType; import com.strava.mediamodels.data.MediaType; import com.strava.photos.data.Media; @@ -166,7 +167,7 @@ public final class AddMediaDownloadPatch { } private static String getString(String name, String fallback) { - int id = Utils.getResourceIdentifier(name, "string"); + int id = Utils.getResourceIdentifier(ResourceType.STRING, name); return id != 0 ? Utils.getResourceString(id) : fallback; 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 static app.revanced.extension.twitch.Utils.getStringId; 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 class TwitchActivityHook { 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/build.gradle.kts b/extensions/youtube/build.gradle.kts index f84a54a0d3..c7116666bd 100644 --- a/extensions/youtube/build.gradle.kts +++ b/extensions/youtube/build.gradle.kts @@ -6,6 +6,6 @@ dependencies { android { defaultConfig { - minSdk = 26 + minSdk = 23 } } 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 @@ public final class AlternativeThumbnailsPatch { * 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 androidx.annotation.Nullable; 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 @@ public class ChangeHeaderPatch { 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 class ChangeHeaderPatch { ? "_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.view.ViewGroup; 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 final class HidePlayerOverlayButtonsPatch { 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 final class HidePlayerOverlayButtonsPatch { = 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 @@ import app.revanced.extension.youtube.settings.Settings; @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 androidx.annotation.Nullable; 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 final class MiniplayerPatch { * 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 final class MiniplayerPatch { 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 static app.revanced.extension.youtube.shared.NavigationBar.NavigationButt 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 final class NavigationButtonsPatch { 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 class OpenShortsInRegularPlayerPatch { 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 @@ public class OpenVideosFullscreenHookPatch { /** * 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 class PlayerControlsPatch { 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..bb0c7dec9b 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 @@ -16,7 +16,7 @@ import java.util.Objects; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; -import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilter; +import app.revanced.extension.youtube.patches.litho.ReturnYouTubeDislikeFilter; import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.PlayerType; @@ -131,6 +131,10 @@ public class ReturnYouTubeDislikePatch { 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 @@ package app.revanced.extension.youtube.patches; import android.app.Activity; -import androidx.annotation.Nullable; - import java.lang.ref.WeakReference; import java.util.Objects; @@ -78,7 +76,7 @@ public class ShortsAutoplayPatch { /** * Injection point. */ - public static Enum changeShortsRepeatBehavior(@Nullable Enum original) { + public static Enum changeShortsRepeatBehavior(Enum original) { try { final boolean autoplay; @@ -95,19 +93,19 @@ public class ShortsAutoplayPatch { 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 class ShortsAutoplayPatch { 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 @@ public class VersionCheckPatch { 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/AdsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/AdsFilter.java similarity index 99% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/AdsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/AdsFilter.java index 063fc53df1..064a7f7e9b 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/AdsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/AdsFilter.java @@ -1,4 +1,4 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; import static app.revanced.extension.shared.StringRef.str; @@ -8,10 +8,10 @@ import android.view.View; import java.util.List; +import app.revanced.extension.shared.patches.litho.Filter; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.StringTrieSearch; -import app.revanced.extension.shared.patches.litho.Filter; import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; import app.revanced.extension.youtube.settings.Settings; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/AdvancedVideoQualityMenuFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/AdvancedVideoQualityMenuFilter.java similarity index 94% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/AdvancedVideoQualityMenuFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/AdvancedVideoQualityMenuFilter.java index 9c6347c1e9..b47f0939d1 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/AdvancedVideoQualityMenuFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/AdvancedVideoQualityMenuFilter.java @@ -1,4 +1,4 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; import app.revanced.extension.shared.patches.litho.Filter; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; 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/litho/ButtonsFilter.java similarity index 92% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ButtonsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ButtonsFilter.java index bc45b50fa0..557847a00d 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/litho/ButtonsFilter.java @@ -1,7 +1,8 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; -import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList; import app.revanced.extension.shared.patches.litho.Filter; +import app.revanced.extension.youtube.patches.VersionCheckPatch; +import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList; import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; import app.revanced.extension.youtube.settings.Settings; @@ -42,7 +43,6 @@ public final class ButtonsFilter extends Filter { addPathCallbacks( likeSubscribeGlow, - bufferFilterPathGroup, new StringFilterGroup( Settings.HIDE_LIKE_DISLIKE_BUTTON, "|segmented_like_dislike_button" @@ -61,6 +61,12 @@ public final class ButtonsFilter extends Filter { ) ); + // 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, @@ -112,11 +118,13 @@ public final class ButtonsFilter extends Filter { } 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/CommentsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/CommentsFilter.java similarity index 98% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/CommentsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/CommentsFilter.java index 1196db546f..793eb5f9b0 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/CommentsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/CommentsFilter.java @@ -1,4 +1,4 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; import app.revanced.extension.shared.patches.litho.Filter; import app.revanced.extension.shared.patches.litho.FilterGroup.*; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/DescriptionComponentsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter.java similarity index 98% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/DescriptionComponentsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter.java index 42c7d67ab7..771902ccef 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/DescriptionComponentsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter.java @@ -1,9 +1,9 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; -import app.revanced.extension.shared.StringTrieSearch; import app.revanced.extension.shared.patches.litho.Filter; +import app.revanced.extension.shared.StringTrieSearch; import app.revanced.extension.shared.patches.litho.FilterGroup.*; -import app.revanced.extension.shared.patches.litho.FilterGroupList.*; +import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.PlayerType; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/HideInfoCardsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/HideInfoCardsFilter.java similarity index 76% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/HideInfoCardsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/HideInfoCardsFilter.java index dc9cf8e56e..3a14a052c9 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/HideInfoCardsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/HideInfoCardsFilter.java @@ -1,8 +1,8 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; +import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.shared.patches.litho.Filter; -import app.revanced.extension.shared.patches.litho.FilterGroup.*; @SuppressWarnings("unused") public final class HideInfoCardsFilter extends Filter { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/KeywordContentFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/KeywordContentFilter.java similarity index 99% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/KeywordContentFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/KeywordContentFilter.java index ad9d20e4c1..93fd1734d4 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/KeywordContentFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/KeywordContentFilter.java @@ -1,4 +1,4 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton; 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/litho/LayoutComponentsFilter.java similarity index 88% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LayoutComponentsFilter.java index 7d2a202924..16d4c166b3 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/litho/LayoutComponentsFilter.java @@ -1,5 +1,6 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; +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; @@ -7,16 +8,17 @@ import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.view.View; -import android.widget.ImageView; +import android.widget.ImageView; import androidx.annotation.Nullable; import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.StringTrieSearch; +import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.patches.litho.Filter; -import app.revanced.extension.shared.patches.litho.FilterGroup.*; -import app.revanced.extension.shared.patches.litho.FilterGroupList.*; +import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; +import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; +import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList; import app.revanced.extension.youtube.patches.ChangeHeaderPatch; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.NavigationBar; @@ -51,7 +53,7 @@ public final class LayoutComponentsFilter extends Filter { private final StringFilterGroup compactChannelBarInnerButton; private final ByteArrayFilterGroup joinMembershipButton; private final StringFilterGroup horizontalShelves; - private final ByteArrayFilterGroup ticketShelf; + private final ByteArrayFilterGroup ticketShelfBuffer; private final StringFilterGroup chipBar; private final StringFilterGroup channelProfile; private final ByteArrayFilterGroupList channelProfileBuffer; @@ -210,7 +212,7 @@ public final class LayoutComponentsFilter extends Filter { // Playable horizontal shelf header. playablesBuffer = new ByteArrayFilterGroup( - Settings.HIDE_PLAYABLES, + null, "FEmini_app_destination" ); @@ -296,15 +298,15 @@ public final class LayoutComponentsFilter extends Filter { ); horizontalShelves = new StringFilterGroup( - Settings.HIDE_HORIZONTAL_SHELVES, + null, // Setting is checked in isFiltered() "horizontal_video_shelf.e", "horizontal_shelf.e", "horizontal_shelf_inline.e", "horizontal_tile_shelf.e" ); - ticketShelf = new ByteArrayFilterGroup( - Settings.HIDE_TICKET_SHELF, + ticketShelfBuffer = new ByteArrayFilterGroup( + null, "ticket_item.e" ); @@ -346,7 +348,7 @@ public final class LayoutComponentsFilter extends Filter { @Override public boolean isFiltered(String identifier, String path, byte[] buffer, - StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { + StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { // This identifier is used not only in players but also in search results: // https://github.com/ReVanced/revanced-patches/issues/3245 // Until 2024, medical information panels such as Covid 19 also used this identifier and were shown in the search results. @@ -386,9 +388,19 @@ public final class LayoutComponentsFilter extends Filter { } if (matchedGroup == horizontalShelves) { - return contentIndex == 0 && (hideShelves() - || ticketShelf.check(buffer).isFiltered() - || playablesBuffer.check(buffer).isFiltered()); + if (contentIndex != 0) return false; + final boolean hideShelves = Settings.HIDE_HORIZONTAL_SHELVES.get(); + final boolean hideTickets = Settings.HIDE_TICKET_SHELF.get(); + final boolean hidePlayables = Settings.HIDE_PLAYABLES.get(); + + if (!hideShelves && !hideTickets && !hidePlayables) return false; + + // Must always check other buffers first, to prevent incorrectly hiding them + // if they are set to show but hide horizontal shelves is set to hidden. + if (ticketShelfBuffer.check(buffer).isFiltered()) return hideTickets; + if (playablesBuffer.check(buffer).isFiltered()) return hidePlayables; + + return hideShelves && hideShelves(); } if (matchedGroup == chipBar) { @@ -402,20 +414,22 @@ public final class LayoutComponentsFilter extends Filter { * 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. // @@ -478,11 +492,23 @@ public final class LayoutComponentsFilter extends Filter { : 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(); @@ -507,6 +533,8 @@ public final class LayoutComponentsFilter extends Filter { && NavigationBar.isSearchBarActive() // Search bar can be active but behind the player. && !PlayerType.getCurrent().isMaximizedOrFullscreen()) { + // FIXME: "Show more" button is visible hidden, + // but an empty space remains that can be clicked. Utils.hideViewByLayoutParams(view); } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/PlaybackSpeedMenuFilter.java similarity index 96% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/PlaybackSpeedMenuFilter.java index 0dc860fbde..05b2dda470 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/PlaybackSpeedMenuFilter.java @@ -1,4 +1,4 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; import app.revanced.extension.shared.patches.litho.Filter; import app.revanced.extension.shared.patches.litho.FilterGroup.*; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlayerFlyoutMenuItemsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/PlayerFlyoutMenuItemsFilter.java similarity index 94% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlayerFlyoutMenuItemsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/PlayerFlyoutMenuItemsFilter.java index 1e23a4db51..a8b88fa8f6 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/PlayerFlyoutMenuItemsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/PlayerFlyoutMenuItemsFilter.java @@ -1,11 +1,12 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; +import app.revanced.extension.shared.patches.litho.Filter; +import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; +import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; +import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch; -import app.revanced.extension.shared.patches.litho.Filter; -import app.revanced.extension.shared.patches.litho.FilterGroup.*; -import app.revanced.extension.shared.patches.litho.FilterGroupList.*; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.ShortsPlayerState; 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/litho/ReturnYouTubeDislikeFilter.java similarity index 87% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ReturnYouTubeDislikeFilter.java index d10efc5c03..ba1d44d017 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/litho/ReturnYouTubeDislikeFilter.java @@ -1,21 +1,21 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; import androidx.annotation.GuardedBy; 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.shared.patches.litho.Filter; +import app.revanced.extension.shared.patches.litho.FilterGroup.*; +import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList; 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; -import app.revanced.extension.shared.patches.litho.Filter; -import app.revanced.extension.shared.patches.litho.FilterGroup.*; -import app.revanced.extension.shared.patches.litho.FilterGroupList.*; /** * Searches for video id's in the proto buffer of Shorts dislike. @@ -36,18 +36,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. @@ -88,7 +77,7 @@ public final class ReturnYouTubeDislikeFilter extends Filter { @Override public boolean isFiltered(String identifier, String path, byte[] buffer, - StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { + StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { if (!Settings.RYD_ENABLED.get() || !Settings.RYD_SHORTS.get()) { return false; } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ShortsFilter.java similarity index 76% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java rename to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ShortsFilter.java index 31b3470c5b..bd07da6a76 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/litho/ShortsFilter.java @@ -1,19 +1,24 @@ -package app.revanced.extension.youtube.patches.components; +package app.revanced.extension.youtube.patches.litho; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton; import android.view.View; +import app.revanced.extension.shared.patches.litho.Filter; +import app.revanced.extension.shared.patches.litho.FilterGroup.*; +import app.revanced.extension.shared.patches.litho.FilterGroup.ByteArrayFilterGroup; +import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilterGroupList; +import app.revanced.extension.shared.settings.BooleanSetting; import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar; import java.lang.ref.WeakReference; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.patches.litho.Filter; -import app.revanced.extension.shared.patches.litho.FilterGroup.*; -import app.revanced.extension.shared.patches.litho.FilterGroupList.*; +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; @@ -21,12 +26,32 @@ import app.revanced.extension.youtube.shared.PlayerType; @SuppressWarnings("unused") public final class ShortsFilter extends Filter { private static final boolean HIDE_SHORTS_NAVIGATION_BAR = Settings.HIDE_SHORTS_NAVIGATION_BAR.get(); - private static final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar.e"; + private static final String COMPONENT_TYPE = "ComponentType"; + private static final String[] REEL_ACTION_BAR_PATHS = { + "reel_action_bar.", // Regular Shorts. + "reels_player_overlay_layout." // Shorts ads. + }; + private static final Map REEL_ACTION_BUTTONS_MAP = new HashMap<>() { + { + // Like button and Dislike button can be hidden with Litho filter. + // put(0, Settings.HIDE_SHORTS_LIKE_BUTTON); + // put(1, Settings.HIDE_SHORTS_DISLIKE_BUTTON); + put(2, Settings.HIDE_SHORTS_COMMENTS_BUTTON); + put(3, Settings.HIDE_SHORTS_SHARE_BUTTON); + put(4, Settings.HIDE_SHORTS_REMIX_BUTTON); + } + }; + private final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar.e"; /** * For paid promotion label and subscribe button that appears in the channel bar. */ - private static final String REEL_METAPANEL_PATH = "reel_metapanel.e"; + private final String REEL_METAPANEL_PATH = "reel_metapanel.e"; + + /** + * For paid promotion label and subscribe button that appears in the channel bar. + */ + private final String REEL_PLAYER_OVERLAY_PATH = "reel_player_overlay.e"; /** * Tags that appears when opening the Shorts player. @@ -46,6 +71,8 @@ public final class ShortsFilter extends Filter { private final ByteArrayFilterGroup useSoundButtonBuffer; private final StringFilterGroup useTemplateButton; private final ByteArrayFilterGroup useTemplateButtonBuffer; + private final StringFilterGroup reelCarousel; + private final ByteArrayFilterGroup reelCarouselBuffer; private final StringFilterGroup autoDubbedLabel; private final StringFilterGroup subscribeButton; @@ -149,13 +176,15 @@ public final class ShortsFilter extends Filter { StringFilterGroup likeButton = new StringFilterGroup( Settings.HIDE_SHORTS_LIKE_BUTTON, "shorts_like_button.e", - "reel_like_button.e" + "reel_like_button.e", + "reel_like_toggled_button.e" ); StringFilterGroup dislikeButton = new StringFilterGroup( Settings.HIDE_SHORTS_DISLIKE_BUTTON, "shorts_dislike_button.e", - "reel_dislike_button.e" + "reel_dislike_button.e", + "reel_dislike_toggled_button.e" ); StringFilterGroup previewComment = new StringFilterGroup( @@ -174,7 +203,7 @@ public final class ShortsFilter extends Filter { autoDubbedLabel = new StringFilterGroup( Settings.HIDE_SHORTS_AUTO_DUBBED_LABEL, - "badge." + "badge.e" ); joinButton = new StringFilterGroup( @@ -199,6 +228,16 @@ public final class ShortsFilter extends Filter { "reel_action_bar.e" ); + reelCarousel = new StringFilterGroup( + Settings.HIDE_SHORTS_SOUND_METADATA_LABEL, + "reel_carousel.e" + ); + + reelCarouselBuffer = new ByteArrayFilterGroup( + null, + "FEsfv_audio_pivot" + ); + useSoundButton = new StringFilterGroup( Settings.HIDE_SHORTS_USE_SOUND_BUTTON, // First filter needed for "Use this sound" that can appear when viewing Shorts @@ -226,7 +265,11 @@ public final class ShortsFilter extends Filter { 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" ); @@ -236,32 +279,39 @@ public final class ShortsFilter extends Filter { ); addPathCallbacks( - shortsCompactFeedVideo, joinButton, subscribeButton, paidPromotionLabel, autoDubbedLabel, - shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar, previewComment, - fullVideoLinkLabel, videoTitle, useSoundButton, reelSoundMetadata, soundButton, infoPanel, - stickers, likeFountain, likeButton, dislikeButton, livePreview + shortsCompactFeedVideo, joinButton, subscribeButton, paidPromotionLabel, livePreview, + suggestedAction, pausedOverlayButtons, channelBar, previewComment, autoDubbedLabel, + fullVideoLinkLabel, videoTitle, useSoundButton, reelSoundMetadata, soundButton, reelCarousel, + 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" - ) - ); + // Legacy hiding of Shorts action buttons. Because of 20.31+ buffer changes + // it's currently not possible to hide these using buffer filtering. + // See alternative hiding strategy in hideActionButtons(). + 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. @@ -275,7 +325,8 @@ public final class ShortsFilter extends Filter { ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_SHOP_BUTTON, - "yt_outline_bag_" + "yt_outline_bag_", + "yt_outline_experimental_bag_" ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_TAGGED_PRODUCTS, @@ -285,31 +336,38 @@ public final class ShortsFilter extends Filter { ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_LOCATION_LABEL, - "yt_outline_location_point_" + "yt_outline_location_point_", + "yt_outline_experimental_location_point_" ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_SAVE_SOUND_BUTTON, "yt_outline_bookmark_", // 'Save sound' button. It seems this has been removed and only 'Save music' is used. // Still hide this in case it's still present. - "yt_outline_list_add_" + "yt_outline_list_add_", + "yt_outline_experimental_list_add_" ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_SEARCH_SUGGESTIONS, - "yt_outline_search_" + "yt_outline_search_", + "yt_outline_experimental_search_" + ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_SUPER_THANKS_BUTTON, - "yt_outline_dollar_sign_heart_" + "yt_outline_dollar_sign_heart_", + "yt_outline_experimental_dollar_sign_heart_" ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_USE_TEMPLATE_BUTTON, // "Use this template" can appear in two different places. - "yt_outline_template_add_" + "yt_outline_template_add_", + "yt_outline_experimental_template_add_" ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_UPCOMING_BUTTON, - "yt_outline_bell_" + "yt_outline_bell_", + "yt_outline_experimental_bell_" ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_EFFECT_BUTTON, @@ -322,11 +380,13 @@ public final class ShortsFilter extends Filter { ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_NEW_POSTS_BUTTON, - "yt_outline_box_pencil" + "yt_outline_box_pencil", + "yt_outline_experimental_box_pencil" ), new ByteArrayFilterGroup( Settings.HIDE_SHORTS_HASHTAG_BUTTON, - "yt_outline_hashtag_" + "yt_outline_hashtag_", + "yt_outline_experimental_hashtag_" ) ); } @@ -343,12 +403,17 @@ public final class ShortsFilter extends Filter { @Override public boolean isFiltered(String identifier, String path, byte[] buffer, - StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { + StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { if (contentType == FilterContentType.PATH) { if (matchedGroup == subscribeButton || matchedGroup == joinButton || matchedGroup == paidPromotionLabel || matchedGroup == autoDubbedLabel) { // Selectively filter to avoid false positive filtering of other subscribe/join buttons. - return path.startsWith(REEL_CHANNEL_BAR_PATH) || path.startsWith(REEL_METAPANEL_PATH); + return path.startsWith(REEL_CHANNEL_BAR_PATH) || path.startsWith(REEL_METAPANEL_PATH) + || path.startsWith(REEL_PLAYER_OVERLAY_PATH); + } + + if (matchedGroup == reelCarousel) { + return reelCarouselBuffer.check(buffer).isFiltered(); } if (matchedGroup == useSoundButton) { @@ -444,6 +509,9 @@ public final class ShortsFilter extends Filter { }; } + /** + * Injection point. + */ public static int getSoundButtonSize(int original) { if (Settings.HIDE_SHORTS_SOUND_BUTTON.get()) { return 0; @@ -452,10 +520,16 @@ public final class ShortsFilter extends Filter { return original; } + /** + * Injection point. + */ public static void setNavigationBar(PivotBar view) { pivotBarRef = new WeakReference<>(view); } + /** + * Injection point. + */ public static void hideNavigationBar(String tag) { if (HIDE_SHORTS_NAVIGATION_BAR) { if (REEL_WATCH_FRAGMENT_INIT_PLAYBACK.contains(tag)) { @@ -470,6 +544,9 @@ public final class ShortsFilter extends Filter { } } + /** + * Injection point. + */ public static int getNavigationBarHeight(int original) { if (HIDE_SHORTS_NAVIGATION_BAR) { return HIDDEN_NAVIGATION_BAR_VERTICAL_HEIGHT; @@ -477,4 +554,4 @@ public final class ShortsFilter extends Filter { return original; } -} +} \ No newline at end of file diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/AdvancedVideoQualityMenuPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/AdvancedVideoQualityMenuPatch.java index 89f221b3be..4a987a6687 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/AdvancedVideoQualityMenuPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/quality/AdvancedVideoQualityMenuPatch.java @@ -8,7 +8,7 @@ import android.widget.ListView; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; -import app.revanced.extension.youtube.patches.components.AdvancedVideoQualityMenuFilter; +import app.revanced.extension.youtube.patches.litho.AdvancedVideoQualityMenuFilter; import app.revanced.extension.youtube.settings.Settings; /** 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/playback/speed/CustomPlaybackSpeedPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java index 32c8354649..198a05c1c8 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch.java @@ -32,7 +32,7 @@ import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.ui.Dim; import app.revanced.extension.shared.ui.SheetBottomDialog; import app.revanced.extension.youtube.patches.VideoInformation; -import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilter; +import app.revanced.extension.youtube.patches.litho.PlaybackSpeedMenuFilter; import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.shared.PlayerType; import kotlin.Unit; 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.Arrays; 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 final class SeekbarColorPatch { 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 @@ public class ReturnYouTubeDislike { // 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 @@ public class ReturnYouTubeDislike { 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 f8c13b83cf..d2b41b229b 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 @@ -19,7 +19,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerH import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType; import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType; import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability; -import static app.revanced.extension.youtube.patches.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability; +import static app.revanced.extension.youtube.patches.litho.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability; import static app.revanced.extension.youtube.patches.spoof.SpoofVideoStreamsPatch.SpoofClientAv1Availability; import static app.revanced.extension.youtube.patches.theme.ThemePatch.SplashScreenAnimationStyle; import static app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController.SponsorBlockDuration; @@ -46,7 +46,7 @@ import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrow 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; @@ -188,7 +188,7 @@ public class Settings extends YouTubeAndMusicSettings { 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()); @@ -284,6 +284,7 @@ public class Settings extends YouTubeAndMusicSettings { 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); @@ -337,6 +338,7 @@ public class Settings extends YouTubeAndMusicSettings { 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); @@ -472,6 +474,13 @@ public class Settings extends YouTubeAndMusicSettings { 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); @@ -514,6 +523,14 @@ public class Settings extends YouTubeAndMusicSettings { 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.preference.PreferenceFragment; 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.preference.YouTubePreferenceFragm 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 @@ public class YouTubeActivityHook extends BaseActivityHook { 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 class YouTubeActivityHook extends BaseActivityHook { 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.CountDownLatch; 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 final class NavigationBar { */ 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 @@ public final class NavigationBar { } /** - * 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 @@ public final class NavigationBar { */ @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 android.widget.TextView; 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 class SegmentPlaybackController { 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 class SegmentPlaybackController { * 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 @@ public class SegmentPlaybackController { * 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 class SegmentPlaybackController { 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 @@ public class SegmentPlaybackController { 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 @@ public class SegmentPlaybackController { * 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 class CreateSegmentButton { 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 android.widget.ImageButton; 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 final class NewSegmentLayout extends FrameLayout { 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 final class NewSegmentLayout extends FrameLayout { */ 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 androidx.annotation.NonNull; 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 class SkipSponsorButton extends FrameLayout { 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 class SkipSponsorButton extends FrameLayout { 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.lang.ref.WeakReference; 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 class SponsorBlockViewController { 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 class SponsorBlockViewController { }); 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 @@ package app.revanced.extension.youtube.videoplayer; 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.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.youtube.patches.VideoInformation; import app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch; diff --git a/gradle.properties b/gradle.properties index 345a486388..74c143b391 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,5 +2,6 @@ org.gradle.caching = true org.gradle.jvmargs = -Xms512M -Xmx2048M org.gradle.parallel = true android.useAndroidX = true +android.uniquePackageNames=false kotlin.code.style = official version = 5.51.0-dev.2 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b3d9a0a352..bea84117e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,35 +1,23 @@ [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" -# 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818. -#noinspection GradleDependency +smali = "3.0.8" agp = "8.2.2" annotation = "1.9.1" -appcompat = "1.7.0" -okhttp = "5.0.0-alpha.14" -retrofit = "2.11.0" +appcompat = "1.7.1" +okhttp = "5.3.2" +retrofit = "3.0.0" guava = "33.5.0-jre" -protobuf-javalite = "4.32.0" -protoc = "4.32.0" -protobuf = "0.9.5" -antlr4 = "4.13.2" -nanohttpd = "2.3.1" -apksig = "8.10.1" +apksig = "9.0.1" [libraries] annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } -antlr4 = { module = "org.antlr:antlr4", version.ref = "antlr4" } appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } -nanohttpd = { module = "org.nanohttpd:nanohttpd", version.ref = "nanohttpd" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } -protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobuf-javalite" } -protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } guava = { module = "com.google.guava:guava", version.ref = "guava" } apksig = { group = "com.android.tools.build", name = "apksig", version.ref = "apksig" } [plugins] android-library = { id = "com.android.library" } -protobuf = { id = "com.google.protobuf", version.ref = "protobuf" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5205bd7954..37f78a6af8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Mon Jun 16 14:39:32 CEST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/patches/api/patches.api b/patches/api/patches.api index 96f779698e..9d48fa58ce 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1,21 +1,17 @@ -public final class DisableReelsScrollingPatchKt { - public static final fun getDisableReelsScrollingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatchKt { - public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/all/misc/adb/HideAdbPatchKt { - public static final fun getHideAdbStatusPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +public final class app/revanced/patches/all/misc/adb/HideAdbStatusPatchKt { + public static final fun getHideADBStatusPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/appicon/HideAppIconPatchKt { - public static final fun getHideAppIconPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getHideAppIconPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt { - public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/build/BuildInfo { @@ -49,63 +45,52 @@ public final class app/revanced/patches/all/misc/build/BuildInfo { } public final class app/revanced/patches/all/misc/build/SpoofBuildInfoPatchKt { - public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatchKt { - public static final fun getHideMockLocationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatchKt { - public static final fun getSpoofSimCountryPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideMockLocationPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatchKt { - public static final fun getSpoofSimProviderPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofSIMProviderPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatchKt { - public static final fun getSpoofWifiPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofWiFiConnectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/customcertificates/CustomCertificatesPatchKt { - public static final fun getCustomNetworkSecurityPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getCustomNetworkSecurityPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatchKt { - 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 static final fun getEnableAndroidDebuggingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/directory/documentsprovider/ExportInternalDataDocumentsProviderPatchKt { - public static final fun getExportInternalDataDocumentsProviderPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getExportInternalDataDocumentsProviderPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/hex/HexPatchKt { - public static final fun getHexPatch ()Lapp/revanced/patcher/patch/RawResourcePatch; + public static final fun getHex ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatchKt { - public static final fun getPredictiveBackGesturePatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getPredictiveBackGesturePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/network/OverrideCertificatePinningPatchKt { - public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatchKt { - public static field packageNameOption Lapp/revanced/patcher/patch/Option; - public static final fun getChangePackageNamePatch ()Lapp/revanced/patcher/patch/ResourcePatch; - public static final fun getPackageNameOption ()Lapp/revanced/patcher/patch/Option; + public static final fun getChangePackageNamePatch ()Lapp/revanced/patcher/patch/Patch; public static final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String; - public static final fun setPackageNameOption (Lapp/revanced/patcher/patch/Option;)V } public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt { - public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt { @@ -117,31 +102,31 @@ public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt { public static final fun addResources (Ljava/lang/String;Ljava/util/List;)Z public static synthetic fun addResources$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Z public static synthetic fun addResources$default (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;ILjava/lang/Object;)Z - public static final fun getAddResourcesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getAddResourcesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatchKt { - public static final fun getRemoveScreenCaptureRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveScreenCaptureRestrictionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/screenshot/PreventScreenshotDetectionPatchKt { - public static final fun getPreventScreenshotDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPreventScreenshotDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatchKt { - public static final fun getRemoveScreenshotRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveScreenshotRestrictionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatchKt { - public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofingKt { - public static final fun getEnableRomSignatureSpoofing ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getEnableROMSignatureSpoofingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt { - public static final fun getSetTargetSdkVersion34 ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getSetTargetSDKVersion34Patch ()Lapp/revanced/patcher/patch/Patch; } public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall { @@ -149,408 +134,371 @@ public abstract interface class app/revanced/patches/all/misc/transformation/IMe public abstract fun getMethodName ()Ljava/lang/String; public abstract fun getMethodParams ()[Ljava/lang/String; public abstract fun getReturnType ()Ljava/lang/String; - public abstract fun replaceInvokeVirtualWithExtension (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V + public fun replaceInvokeVirtualWithExtension (Ljava/lang/String;Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V } public final class app/revanced/patches/all/misc/transformation/IMethodCall$DefaultImpls { - public static fun replaceInvokeVirtualWithExtension (Lapp/revanced/patches/all/misc/transformation/IMethodCall;Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V + public static fun replaceInvokeVirtualWithExtension (Lapp/revanced/patches/all/misc/transformation/IMethodCall;Ljava/lang/String;Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V } public final class app/revanced/patches/all/misc/transformation/TransformInstructionsPatchKt { - public static final fun transformInstructionsPatch (Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun transformInstructionsPatch (Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatchKt { - public static final fun getChangeVersionCodePatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getChangeVersionCodePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/amazon/DeepLinkingPatchKt { - public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getAlwaysAllowDeepLinkingPatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatchKt { - public static final fun getRemovePlayLimitsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemovePlayLimitsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatchKt { - public static final fun getBypassRootChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBypassRootChecksPatch ()Lapp/revanced/patcher/patch/Patch; +} + +public final class app/revanced/patches/com/sbs/ondemand/tv/FingerprintsKt { + public static final fun getShouldShowAdvertisingTVMethod (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod; } public final class app/revanced/patches/com/sbs/ondemand/tv/RemoveAdsPatchKt { - public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/cricbuzz/ads/DisableAdsPatchKt { - public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/cricbuzz/misc/extension/ExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/crunchyroll/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/disneyplus/ads/SkipAdsPatchKt { - public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +public final class app/revanced/patches/disneyplus/SkipAdsPatchKt { + public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/duolingo/ad/DisableAdsPatchKt { - public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatchKt { - public static final fun getEnableDebugMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableDebugMenuPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/duolingo/energy/SkipEnergyRechargeAdsPatchKt { - public static final fun getSkipEnergyRechargeAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSkipEnergyRechargeAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatchKt { - public static final fun getHideSponsoredStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideSponsoredStoriesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/facebook/ads/story/HideStoryAdsPatchKt { - public static final fun getHideStoryAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideStoryAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatchKt { - public static final fun getBootloaderDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveBootloaderDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/finanzonline/detection/root/RootDetectionPatchKt { - public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveRootDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/fotmob/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/gmxmail/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/gmxmail/freephone/ForceEnableFreephonePatchKt { - public static final fun getForceEnableFreePhonePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getForceEnableFreePhonePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/gmxmail/layout/HidePremiumUpgradeButtonKt { - public static final fun getHidePremiumUpgradeButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHidePremiumUpgradeButtonPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatchKt { - public static final fun getEnableCustomTabsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableCustomTabsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlenews/misc/extension/ExtensionPatchKt { - public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatchKt { - public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlephotos/misc/backup/EnableDCIMFoldersBackupControlPatchKt { - public static final fun getEnableDCIMFoldersBackupControlPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableDCIMFoldersBackupControlPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlephotos/misc/extension/ExtensionPatchKt { - public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatchKt { - public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatchKt { - public static final fun getSpoofFeaturesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofFeaturesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatchKt { - 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 static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictionsKt { - public static final fun getRemoveDeviceRestrictionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveDeviceRestrictionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/hexeditor/ad/DisableAdsPatchKt { - public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/idaustria/detection/deviceintegrity/RemoveDeviceIntegrityChecksPatchKt { - public static final fun getRemoveDeviceIntegrityChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/idaustria/detection/root/RootDetectionPatchKt { - public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveDeviceIntegrityChecksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatchKt { - public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/inshorts/ad/InshortsAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/feed/LimitFeedToFollowedProfilesKt { - public static final fun getLimitFeedToFollowedProfiles ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getLimitFeedToFollowedProfilesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/ghost/story/AnonymousStoryViewingPatchKt { - public static final fun getAnonymousStoryViewingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getAnonymousStoryViewingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/hide/explore/HideExploreFeedKt { - public static final fun getHideExploreFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideExploreFeedPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/hide/highlightsTray/HideHighlightsTrayPatchKt { - public static final fun getHideHighlightsTrayPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideHighlightsTrayPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/hide/navigation/HideNavigationButtonsKt { - public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/hide/stories/HideStoriesKt { - public static final fun getHideStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideStoriesFromHomePatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/instagram/hide/suggestions/HideSuggestedContentKt { - public static final fun getHideSuggestedContent ()Lapp/revanced/patcher/patch/BytecodePatch; +public final class app/revanced/patches/instagram/hide/suggestions/HideSuggestedContentPatchKt { + public static final fun getHideSuggestedContentPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/devmenu/EnableDeveloperMenuPatchKt { - public static final fun getEnableDeveloperMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableDeveloperMenuPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/disableAnalytics/DisableAnalyticsPatchKt { - public static final fun getDisableAnalyticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAnalyticsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/links/OpenLinksExternallyPatchKt { - public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/removeBuildExpiredPopup/RemoveBuildExpiredPopupPatchKt { - public static final fun getRemoveBuildExpiredPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveBuildExpiredPopupPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatchKt { - public static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/share/privacy/SanitizeSharingLinksPatchKt { - public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/misc/signature/SignatureCheckPatchKt { - public static final fun getSignatureCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableSignatureCheckPatch ()Lapp/revanced/patcher/patch/Patch; +} + +public final class app/revanced/patches/instagram/reels/DisableReelsScrollingPatchKt { + public static final fun getDisableReelsScrollingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/instagram/story/flipping/DisableStoryAutoFlippingPatchKt { - public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt { - public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/kleinanzeigen/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/kleinanzeigen/hide_pur/HidePurPatchKt { - public static final fun getHidePurPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHidePurPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/letterboxd/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatchKt { - public static final fun getUnlockAppIconsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockAppIconsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt { - public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatchKt { - public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/lightroom/misc/version/DisableVersionCheckPatchKt { - public static final fun getDisableVersionCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableVersionCheckPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/memegenerator/detection/license/LicenseValidationPatchKt { - public static final fun getLicenseValidationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getLicenseValidationPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatchKt { - public static final fun getSignatureVerificationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSignatureVerificationPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatchKt { - public static final fun getUnlockProVersionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/messenger/inbox/HideInboxAdsPatchKt { - public static final fun getHideInboxAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideInboxAdsPatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun getHideInboxSubtabsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/messenger/layout/HideFacebookButtonPatchKt { - public static final fun getHideFacebookButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideFacebookButtonPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/messenger/metaai/RemoveMetaAIPatchKt { - public static final fun getRemoveMetaAIPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveMetaAIPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/messenger/misc/extension/ExtensionPatchKt { - 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 static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/microsoft/officelens/misc/onedrive/HideOneDriveMigrationPatchKt { - public static final fun getHideOneDriveMigrationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideOneDriveMigrationPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt { - public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/mifitness/misc/login/FixLoginPatchKt { - public static final fun getFixLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixLoginPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/ad/video/HideVideoAdsKt { - public static final fun getHideVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideMusicVideoAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlaybackKt { - public static final fun getEnableExclusiveAudioPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableExclusiveAudioPlaybackPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatchKt { - 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 static final fun getPermanentRepeatPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/layout/branding/CustomBrandingPatchKt { - public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun getHideButtonsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/layout/compactheader/HideCategoryBarKt { - public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideCategoryBarPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/layout/hide/general/HideLayoutComponentsPatchKt { - public static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColorKt { - public static final fun getChangeMiniplayerColor ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getChangeMiniplayerColorPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/layout/navigationbar/NavigationBarPatchKt { - public static final fun getNavigationBarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getNavigationBarPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/layout/premium/HideGetPremiumPatchKt { - public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideGetMusicPremiumPatch ()Lapp/revanced/patcher/patch/Patch; } 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; + public static final fun getThemePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/androidauto/UnlockAndroidAutoMediaBrowserPatchKt { - public static final fun getUnlockAndroidAutoMediaBrowserPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockAndroidAutoMediaBrowserPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt { - public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveBackgroundPlaybackRestrictionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/debugging/EnableDebuggingPatchKt { - public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/dns/CheckWatchHistoryDomainNameResolutionPatchKt { - public static final fun getCheckWatchHistoryDomainNameResolutionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getCheckWatchHistoryDomainNameResolutionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/gms/Constants { @@ -558,15 +506,15 @@ public final class app/revanced/patches/music/misc/gms/Constants { } public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt { - public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/litho/filter/LithoFilterPatchKt { - public static final fun getLithoFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getLithoFilterPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/privacy/SanitizeSharingLinksPatchKt { - public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/settings/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen { @@ -579,24 +527,24 @@ public final class app/revanced/patches/music/misc/settings/PreferenceScreen : a } public final class app/revanced/patches/music/misc/settings/SettingsPatchKt { - public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; } public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatchKt { - public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt { - public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/misc/tracks/ForceOriginalAudioPatchKt { - public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/music/playservice/VersionCheckPatchKt { - public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun is_7_16_or_greater ()Z public static final fun is_7_33_or_greater ()Z public static final fun is_8_05_or_greater ()Z @@ -606,319 +554,311 @@ public final class app/revanced/patches/music/playservice/VersionCheckPatchKt { } public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/myfitnesspal/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatchKt { - 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 static final fun getRemoveBroadcastsRestrictionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/nothingx/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/nothingx/misc/logk1token/ShowK1TokenPatchsKt { - public static final fun getShowK1TokensPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getShowK1TokensPatch ()Lapp/revanced/patcher/patch/Patch; +} + +public final class app/revanced/patches/nunl/ads/FingerprintsKt { + public static final fun getJwPlayerConfigMethod (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod; } public final class app/revanced/patches/nunl/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun getSpoofCertificatePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatchKt { - public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/orfon/detection/root/RemoveRootDetectionPatchKt { - public static final fun getRemoveRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveRootDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/pandora/ads/DisableAudioAdsPatchKt { - public static final fun getDisableAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAudioAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatchKt { - public static final fun getEnableUnlimitedSkipsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableUnlimitedSkipsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/peacocktv/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt { - public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofDeviceIDPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/photomath/detection/signature/SignatureDetectionPatchKt { - public static final fun getSignatureDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSignatureDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatchKt { - public static final fun getHideUpdatePopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideUpdatePopupPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatchKt { - public static final fun getEnableBookpointPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableBookpointPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatchKt { - public static final fun getUnlockPlusPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockPlusPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatchKt { - public static final fun getSpoofAndroidDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofAndroidDeviceIDPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/piccomafr/tracking/DisableTrackingPatchKt { - public static final fun getDisableTrackingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableTrackingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/primevideo/ads/SkipAdsPatchKt { - public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/primevideo/misc/extension/ExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/primevideo/misc/permissions/RenamePermissionsPatchKt { - public static final fun getRenamePermissionsPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getRenameSharedPermissionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/primevideo/video/speed/PlaybackSpeedPatchKt { - public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/protonmail/account/RemoveFreeAccountsLimitPatchKt { - public static final fun getRemoveFreeAccountsLimitPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getRemoveFreeAccountsLimitPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatchKt { - public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/protonvpn/delay/RemoveDelayPatchKt { - public static final fun getRemoveDelayPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveDelayPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/protonvpn/splittunneling/UnlockSplitTunnelingKt { - public static final fun getUnlockSplitTunnelingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockSplitTunnelingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatchKt { - public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/reddit/ad/banner/HideBannerPatchKt { - public static final fun getHideBannerPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/ad/comments/HideCommentAdsPatchKt { - public static final fun getHideCommentAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideCommentAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/ad/general/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/FixRedgifsApiPatchKt { public static final field CREATE_NEW_CLIENT_METHOD Ljava/lang/String; public static final field INSTALL_NEW_CLIENT_METHOD Ljava/lang/String; - public static final fun fixRedgifsApiPatch (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; - public static synthetic fun fixRedgifsApiPatch$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun fixRedgifsApiPatch (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/Patch; + public static synthetic fun fixRedgifsApiPatch$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/FixSLinksPatchKt { public static final field RESOLVE_S_LINK_METHOD Ljava/lang/String; public static final field SET_ACCESS_TOKEN_METHOD Ljava/lang/String; - public static final fun fixSLinksPatch (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; - public static synthetic fun fixSLinksPatch$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun fixSLinksPatch (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/Patch; + public static synthetic fun fixSLinksPatch$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/SpoofClientPatchKt { - public static final fun spoofClientPatch (Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/BytecodePatch; - public static synthetic fun spoofClientPatch$default (Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun spoofClientPatch (Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Patch; + public static synthetic fun spoofClientPatch$default (Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/FixRedgifsApiPatchKt { - public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/baconreader/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatchKt { - public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatchKt { - public static final fun getFixAudioMissingInDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInVideoDownloadsPatchKt { + public static final fun getFixMissingAudioInVideoDownloadsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/FixRedgifsApiPatchKt { - public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatchKt { public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String; - public static final fun getFixSlinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixSlinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatchKt { - public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatchKt { - public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatchKt { - public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatchKt { - public static final fun disableAdsPatch (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; - public static synthetic fun disableAdsPatch$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun disableAdsPatch (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/Patch; + public static synthetic fun disableAdsPatch$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatchKt { - public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatchKt { - public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatchKt { - public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatchKt { - public static final fun getDisableSyncForLemmyBottomSheetPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableSyncForLemmyBottomSheetPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatchKt { - public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/FixRedgifsApiPatchKt { - public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatchKt { public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String; - public static final fun getFixSLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixSLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/FixPostThumbnailsPatchKt { - public static final fun getFixPostThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixPostThumbnailsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatchKt { - public static final fun getUseUserEndpointPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUseUserEndpointPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/FixVideoDownloadsPatchKt { - 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 static final fun getFixVideoDownloadsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatchKt { - public static final fun getDisableScreenshotPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableScreenshotPopupPatch ()Lapp/revanced/patcher/patch/Patch; } 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; + public static final fun getUnlockPremiumIconsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/misc/extension/ExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatchKt { - public static final fun getSanitizeUrlQueryPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatchKt { - public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixCrashesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatchKt { - public static final fun getBypassDeviceChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBypassDeviceChecksPatch ()Lapp/revanced/patcher/patch/Patch; +} + +public final class app/revanced/patches/samsung/radio/restrictions/device/FingerprintsKt { + public static final fun getGetCountryIsoMethodReference ()Lcom/android/tools/smali/dexlib2/immutable/reference/ImmutableMethodReference; + public static final fun getGetSalesCodeMethodReference ()Lcom/android/tools/smali/dexlib2/immutable/reference/ImmutableMethodReference; } public final class app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatchKt { - public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveRootDetectionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/shared/layout/theme/LithoColorHookPatchKt { - public static final fun getLithoColorHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getLithoColorHookPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun getLithoColorOverrideHook ()Lkotlin/jvm/functions/Function2; } public final class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatchKt { - public static final fun checkEnvironmentPatch (Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;[Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun checkEnvironmentPatch (Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/patch/Patch;[Ljava/lang/String;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/shared/misc/extension/ExtensionHook { @@ -926,27 +866,22 @@ public final class app/revanced/patches/shared/misc/extension/ExtensionHook { } public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt { - 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 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 final fun activityOnCreateExtensionHook (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; + public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; + public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;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/Patch; + public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/Patch; } 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 static final fun getVerticalScrollPatch ()Lapp/revanced/patcher/patch/Patch; } 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;Lkotlin/Pair;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;Lkotlin/Pair;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 gmsCoreSupportPatch (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Lkotlin/Pair;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/Patch; + public static synthetic fun gmsCoreSupportPatch$default (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Lkotlin/Pair;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch; + 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/Patch; + 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/Patch; } public final class app/revanced/patches/shared/misc/hex/HexPatchBuilder : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker { @@ -972,14 +907,11 @@ public final class app/revanced/patches/shared/misc/hex/HexPatchBuilder : java/u } public final class app/revanced/patches/shared/misc/hex/HexPatchBuilderKt { - public static final fun hexPatch (ZLkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/RawResourcePatch; - public static final fun hexPatch (ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/RawResourcePatch; - public static synthetic fun hexPatch$default (ZLkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lapp/revanced/patcher/patch/RawResourcePatch; - public static synthetic fun hexPatch$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/RawResourcePatch; + public static final fun hexPatch (ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/Patch; + public static synthetic fun hexPatch$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/shared/misc/hex/Replacement { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public fun ([B[BLjava/lang/String;)V public final fun getReplacementBytesPadded ()[B } @@ -989,45 +921,83 @@ public final class app/revanced/patches/shared/misc/litho/filter/LithoFilterPatc } 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 getResourceMappingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; - public static final fun getResourceMappings ()Ljava/util/List; + public static final fun getResourceMappingPatch ()Lapp/revanced/patcher/patch/Patch; +} + +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 final fun get (Ljava/lang/String;)J + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getValue ()Ljava/lang/String; + public final fun invoke (Ljava/lang/String;)Lkotlin/jvm/functions/Function4; + 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 { - public static final fun getDisableLicenseCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisablePairipLicenseCheckPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/shared/misc/privacy/DisableSentryTelemetryKt { - public static final fun getDisableSentryTelemetryPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getDisableSentryTelemetryPatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/Patch; + public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch; } 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; @@ -1051,9 +1021,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; @@ -1062,8 +1033,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; @@ -1071,8 +1042,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; @@ -1091,8 +1062,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 @@ -1122,22 +1093,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; } @@ -1165,8 +1136,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; @@ -1174,261 +1145,233 @@ 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; } public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt { - public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/shared/misc/string/ReplaceStringPatchKt { - public static final fun replaceStringPatch (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun replaceStringPatch (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt { - public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/songpal/badge/BadgeTabPatchKt { - public static final fun getBadgeTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveBadgeTabPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/songpal/badge/RemoveNotificationBadgePatchKt { - public static final fun getRemoveNotificationBadgePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveNotificationBadgePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/soundcloud/ad/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPatchKt { - public static final fun getDisableTelemetryPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableTelemetryPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatchKt { - 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 static final fun getEnableOfflineSyncPatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun getCustomThemePatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/spotify/misc/fix/login/FixFacebookLoginPatchKt { - public static final fun getFixFacebookLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixFacebookLoginPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/spotify/misc/lyrics/ChangeLyricsProviderPatchKt { - public static final fun getChangeLyricsProviderPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getChangeLyricsProviderPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatchKt { - public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgetsKt { - 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 static final fun getFixThirdPartyLaunchersWidgetsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/stocard/layout/HideOffersTabPatchKt { - public static final fun getHideOffersTabPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getHideOffersTabPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatchKt { - public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/distractions/HideDistractionsPatchKt { - public static final fun getHideDistractionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideDistractionsPatch ()Lapp/revanced/patcher/patch/Patch; } -public final class app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityKt { - public static final fun getAddGiveGroupKudosButtonToGroupActivity ()Lapp/revanced/patcher/patch/BytecodePatch; +public final class app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityPatchKt { + public static final fun getAddGiveGroupKudosButtonToGroupActivityPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/media/download/AddMediaDownloadPatchKt { - public static final fun getAddMediaDownloadPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getAddMediaDownloadPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/media/upload/OverwriteMediaUploadParametersPatchKt { - public static final fun getOverwriteMediaUploadParametersPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getOverwriteMediaUploadParametersPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/password/EnablePasswordLoginPatchKt { - public static final fun getEnablePasswordLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnablePasswordLoginPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatchKt { - public static final fun getBlockSnowplowTrackingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBlockSnowplowTrackingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/quickedit/DisableQuickEditPatchKt { - public static final fun getDisableQuickEditPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableQuickEditPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt { - public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; -} - -public final class app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatchKt { - public static final fun getDisableSubscriptionSuggestionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockSubscriptionFeaturesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatchKt { - public static final fun getRemoveGooglePlayIntegrityCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveGooglePlayIntegrityCheckPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/threads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockThemesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/feedfilter/FeedFilterPatchKt { - public static final fun getFeedFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFeedFilterPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatchKt { - public static final fun getRememberClearDisplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRememberClearDisplayPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/interaction/downloads/DownloadsPatchKt { - public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatchKt { - public static final fun getShowSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getShowSeekbarPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatchKt { - public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/misc/extension/ExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatchKt { - public static final fun getDisableLoginRequirementPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableLoginRequirementPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatchKt { - public static final fun getFixGoogleLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixGoogleLoginPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/misc/settings/SettingsPatchKt { - public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatchKt { - public static final fun getSanitizeShareUrlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatchKt { - public static final fun getSpoofSimPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSIMSpoofPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/trakt/UnlockProPatchKt { - public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatchKt { - public static final fun getShowOnLockscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getShowOnLockscreenPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tudortmund/misc/extension/ExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/ads/DisableDashboardAdsKt { - public static final fun getDisableDashboardAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableDashboardAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatchKt { - public static final fun getDisableAdFreeBannerPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAdFreeBannerPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatchKt { - public static final fun getDisableInAppUpdatePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableInAppUpdatePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatchKt { - public static final fun getDisableBlogNotificationReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableBlogNotificationReminderPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatchKt { - public static final fun getDisableGiftMessagePopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableGiftMessagePopupPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/annoyances/tv/DisableTumblrTvPatchKt { - public static final fun getDisableTumblrTvPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableTumblrTVPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatchKt { - public static final fun getOverrideFeatureFlagsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getOverrideFeatureFlagsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/fixes/FixOldVersionsPatchKt { - public static final fun getFixOldVersionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFixOldVersionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/misc/extension/ExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatchKt { public static field addTimelineObjectTypeFilter Lkotlin/jvm/functions/Function1; public static final fun getAddTimelineObjectTypeFilter ()Lkotlin/jvm/functions/Function1; - public static final fun getFilterTimelineObjectsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFilterTimelineObjectsPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun setAddTimelineObjectTypeFilter (Lkotlin/jvm/functions/Function1;)V } public final class app/revanced/patches/twitch/ad/audio/AudioAdsPatchKt { - public static final fun getAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBlockAudioAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatchKt { - public static final fun getEmbeddedAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBlockEmbeddedAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/ad/shared/util/AdPatchKt { - public static final fun adPatch (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function3;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun adPatch (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function3;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/ad/shared/util/ReturnMethod { @@ -1443,274 +1386,245 @@ public final class app/revanced/patches/twitch/ad/shared/util/ReturnMethod$Compa } public final class app/revanced/patches/twitch/ad/video/VideoAdsPatchKt { - public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBlockVideoAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatchKt { - public static final fun getShowDeletedMessagesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getShowDeletedMessagesPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatchKt { - public static final fun getAutoClaimChannelPointsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getAutoClaimChannelPointsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/debug/DebugModePatchKt { - public static final fun getDebugModePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDebugModePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitch/misc/settings/SettingsPatchKt { public static final fun addSettingPreference (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V - public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatchKt { - public static final fun getUnlockDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getUnlockDownloadsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/layout/viewcount/HideViewCountPatchKt { - public static final fun getHideViewCountPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideViewCountPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatchKt { - public static final fun getDynamicColorPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getDynamicColorPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/misc/extension/ExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/misc/hook/HideAdsHookPatchKt { - public static final fun getHideAdsHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsHookPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/misc/hook/HideRecommendedUsersPatchKt { - public static final fun getHideRecommendedUsersPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideRecommendedUsersPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/misc/hook/HookPatchKt { - public static final fun hookPatch (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun hookPatch (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/misc/hook/json/JsonHook { - public fun (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)V } public final class app/revanced/patches/twitter/misc/hook/json/JsonHookPatchKt { public static final fun addJsonHook (Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/patches/twitter/misc/hook/json/JsonHook;)V - public static final fun getJsonHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getJsonHookPatch ()Lapp/revanced/patcher/patch/Patch; + public static final fun jsonHook (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)Lapp/revanced/patches/twitter/misc/hook/json/JsonHook; } public final class app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatchKt { - 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 static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatchKt { - public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/viber/ads/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/viber/misc/navbar/HideNavigationButtonsKt { - 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 static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatchKt { - public static final fun getFirebaseGetCertPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getFirebaseGetCertPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatchKt { - public static final fun getPromoCodeUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPromoCodeUnlockPatch ()Lapp/revanced/patcher/patch/Patch; } -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/willhaben/ads/HideAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/ad/general/HideAdsPatchKt { - public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatchKt { - public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/ad/video/VideoAdsPatchKt { - public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatchKt { - public static final fun getCopyVideoUrlPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getCopyVideoURLPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatchKt { - public static final fun getRemoveViewerDiscretionDialogPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveViewerDiscretionDialogPatch ()Lapp/revanced/patcher/patch/Patch; } 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; + public static final fun getDisableDoubleTapActionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/downloads/DownloadsPatchKt { - public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatchKt { - public static final fun getDisablePreciseSeekingGesturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisablePreciseSeekingGesturePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatchKt { - public static final fun getEnableSeekbarTappingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableSeekbarTappingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatchKt { - public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatchKt { - public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarPatchKt { - public static final fun getSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSeekbarPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatchKt { - public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatchKt { - public static final fun getSwipeControlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSwipeControlsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatchKt { - public static final fun getAutoCaptionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableAutoCaptionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPatchKt { - public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatchKt { - public static final fun getChangeHeaderPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getChangeHeaderPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatchKt { - public static final fun getHideButtonsPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getHideVideoActionButtonsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatchKt { - public static final fun getNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getNavigationButtonsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatchKt { - public static final fun getHidePlayerOverlayButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHidePlayerOverlayButtonsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatchKt { - public static final fun getChangeFormFactorPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getChangeFormFactorPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndScreenCardsPatchKt { - public static final fun getHideEndScreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideEndScreenCardsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatchKt { - public static final fun getHideEndScreenSuggestedVideoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideEndScreenSuggestedVideoPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatchKt { - public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/Patch; } 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 static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatchKt { - public static final fun getHideInfoCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideInfoCardsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatchKt { - public static final fun getHidePlayerFlyoutMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHidePlayerFlyoutMenuItemsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatchKt { - public static final fun getHideRelatedVideoOverlayPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideRelatedVideoOverlayPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatchKt { - 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 static final fun getDisableRollingNumberAnimationsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatchKt { - public static final fun getHideShortsComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideShortsComponentsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/signintotvpopup/DisableSignInToTvPatchPopupKt { - 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 static final fun getDisableSignInToTVPopupPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPatchKt { - public static final fun getHideTimestampPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideTimestampPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatchKt { - public static final fun getMiniplayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getMiniplayerPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatchKt { - public static final fun getPlayerPopupPanelsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisablePlayerPopupPanelsPatch ()Lapp/revanced/patcher/patch/Patch; } -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/ExitFullscreenPatchKt { + public static final fun getExitFullscreenPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatchKt { - public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatchKt { - public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatchKt { - public static final fun getReturnYouTubeDislikePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getReturnYouTubeDislikePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/returnyoutubedislike/Vote : java/lang/Enum { @@ -1724,108 +1638,87 @@ public final class app/revanced/patches/youtube/layout/returnyoutubedislike/Vote } public final class app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatchKt { - public static final fun getWideSearchbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getWideSearchBarPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatchKt { - public static final fun getSeekbarColorPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSeekbarColorPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatchKt { - public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatchKt { - public static final fun getOpenShortsInRegularPlayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getOpenShortsInRegularPlayerPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt { - public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatchKt { - public static final fun getSpoofAppVersionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofAppVersionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatchKt { - public static final fun getChangeStartPagePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getChangeStartPagePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatchKt { - 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 static final fun getDisableResumingShortsOnStartupPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/theme/ThemePatchKt { - public static final fun getThemePatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getThemePatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatchKt { - public static final fun getAlternativeThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getAlternativeThumbnailsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatchKt { - public static final fun getBypassImageRegionRestrictionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBypassImageRegionRestrictionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/announcements/AnnouncementsPatchKt { - public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatchKt { - public static final fun getPauseOnAudioInterruptPatch ()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 static final fun getPauseOnAudioInterruptPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatchKt { - public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemoveBackgroundPlaybackRestrictionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt { - public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatchKt { - public static final fun getSpoofDeviceDimensionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofDeviceDimensionsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatchKt { - public static final fun getCheckWatchHistoryDomainNameResolutionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getCheckWatchHistoryDomainNameResolutionPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/extension/SharedExtensionPatchKt { - public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch; } -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; +public final class app/revanced/patches/youtube/misc/fix/playbackspeed/FixPlaybackSpeedWhilePlayingPatchKt { + public static final fun getFixPlaybackSpeedWhilePlayingPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatchKt { - public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/hapticfeedback/DisableHapticFeedbackPatchKt { - public static final fun getDisableHapticFeedbackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getDisableHapticFeedbackPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHookKt { @@ -1833,54 +1726,49 @@ public final class app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrl public static final fun addImageUrlHook (Ljava/lang/String;Z)V public static synthetic fun addImageUrlHook$default (Ljava/lang/String;ZILjava/lang/Object;)V public static final fun addImageUrlSuccessCallbackHook (Ljava/lang/String;)V - public static final fun getCronetImageUrlHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getCronetImageUrlHookPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatchKt { - public static final fun getBypassURLRedirectsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getBypassURLRedirectsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatchKt { - public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatchKt { - public static final fun getAddLithoFilter ()Lkotlin/jvm/functions/Function1; - public static final fun getLithoFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getLithoFilterPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/loopvideo/LoopVideoPatchKt { - public static final fun getLoopVideoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getLoopVideoPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatchKt { public static field hookNavigationButtonCreated Lkotlin/jvm/functions/Function1; public static final fun getHookNavigationButtonCreated ()Lkotlin/jvm/functions/Function1; - public static final fun getNavigationBarHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getNavigationBarHookPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun setHookNavigationButtonCreated (Lkotlin/jvm/functions/Function1;)V } public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatchKt { - public static final fun getPlayerControlsOverlayVisibilityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlayerControlsOverlayVisibilityPatch ()Lapp/revanced/patcher/patch/Patch; } 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 getPlayerControlsPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun initializeBottomControl (Ljava/lang/String;)V public static final fun injectVisibilityCheckCall (Ljava/lang/String;)V } public final class app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatchKt { - public static final fun getPlayerTypeHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlayerTypeHookPatch ()Lapp/revanced/patcher/patch/Patch; } 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 getVersionCheckPatch ()Lapp/revanced/patcher/patch/Patch; 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 @@ -1905,19 +1793,29 @@ 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 { - public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatchKt { public static final fun getAddRecyclerViewTreeHook ()Lkotlin/jvm/functions/Function1; - public static final fun getRecyclerViewTreeHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRecyclerViewTreeHookPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/settings/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen { @@ -1938,36 +1836,28 @@ public final class app/revanced/patches/youtube/misc/settings/PreferenceScreen : } public final class app/revanced/patches/youtube/misc/settings/SettingsPatchKt { - public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; } public final class app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatchKt { - public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/misc/spoof/UserAgentClientSpoofPatchKt { - 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 static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt { - public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/codecs/DisableVideoCodecsPatchKt { - 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 static final fun getDisableVideoCodecsPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt { - public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V public static final fun videoSpeedChangedHook (Ljava/lang/String;Ljava/lang/String;)V public static final fun videoTimeHook (Ljava/lang/String;Ljava/lang/String;)V @@ -1992,47 +1882,39 @@ public final class app/revanced/patches/youtube/video/playerresponse/Hook$VideoI public final class app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatchKt { public static final fun addPlayerResponseMethodHook (Lapp/revanced/patches/youtube/video/playerresponse/Hook;)V - public static final fun getPlayerResponseMethodHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlayerResponseMethodHookPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/quality/RememberVideoQualityPatchKt { - public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/quality/VideoQualityDialogButtonPatchKt { - public static final fun getVideoQualityDialogButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getVideoQualityDialogButtonPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/quality/VideoQualityPatchKt { - public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt { - public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatchKt { - public static final fun getPlaybackSpeedButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlaybackSpeedButtonPatch ()Lapp/revanced/patcher/patch/Patch; } public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt { - public static final fun getVideoIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getVideoIdPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun hookBackgroundPlayVideoId (Ljava/lang/String;)V public static final fun hookPlayerResponseVideoId (Ljava/lang/String;)V 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 + public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;ILjava/lang/String;)V + public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/extensions/ExternalLabel;)V public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z @@ -2041,8 +1923,7 @@ public final class app/revanced/util/BytecodeUtilsKt { public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List; public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List; public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod; - public static final fun forEachLiteralValueInstruction (Lapp/revanced/patcher/patch/BytecodePatchContext;JLkotlin/jvm/functions/Function2;)V + public static final fun forEachInstructionAsSequence (Lapp/revanced/patcher/patch/BytecodePatchContext;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function2;)V public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;)I public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I @@ -2077,29 +1958,29 @@ public final class app/revanced/util/BytecodeUtilsKt { public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I 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 - public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;F)V - 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;S)V - public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)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 - public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;F)V - 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;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 - public static final fun traverseClassHierarchy (Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V + public static final fun injectHideViewCall (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V + public static final fun literal (Lapp/revanced/patcher/MutablePredicateList;Lkotlin/jvm/functions/Function0;)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;B)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;C)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;D)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;F)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;I)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;J)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;Ljava/lang/String;)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;S)V + public static final fun returnEarly (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;Z)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;B)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;C)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;D)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;F)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;I)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;J)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;Ljava/lang/String;)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;S)V + public static final fun returnLate (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;Z)V + public static final fun transformMethods (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableClassDef;Lkotlin/jvm/functions/Function1;)V + public static final fun traverseClassHierarchy (Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableClassDef;Lkotlin/jvm/functions/Function1;)V } public final class app/revanced/util/ResourceGroup { diff --git a/patches/build.gradle.kts b/patches/build.gradle.kts index 4e5c89da63..b0adaf6da6 100644 --- a/patches/build.gradle.kts +++ b/patches/build.gradle.kts @@ -24,7 +24,10 @@ dependencies { kotlin { compilerOptions { - freeCompilerArgs = listOf("-Xcontext-receivers") + freeCompilerArgs.addAll( + "-Xexplicit-backing-fields", + "-Xcontext-parameters" + ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt index 30193b7049..8336cebdbc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt @@ -8,7 +8,7 @@ val exportAllActivitiesPatch = resourcePatch( description = "Makes all app activities exportable.", use = false, ) { - execute { + apply { val exportedFlag = "android:exported" document("AndroidManifest.xml").use { document -> diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/adb/HideAdbPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/adb/HideAdbStatusPatch.kt similarity index 75% rename from patches/src/main/kotlin/app/revanced/patches/all/misc/adb/HideAdbPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/adb/HideAdbStatusPatch.kt index 28653638bc..71f6777857 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/adb/HideAdbPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/adb/HideAdbStatusPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.all.misc.adb -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.util.getReference @@ -13,27 +13,26 @@ import com.android.tools.smali.dexlib2.util.MethodUtil private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/all/misc/hide/adb/HideAdbPatch;" private val SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE = ImmutableMethodReference( - "Landroid/provider/Settings\$Global;", + $$"Landroid/provider/Settings$Global;", "getInt", listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;"), - "I" + "I", ) private val SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE = ImmutableMethodReference( - "Landroid/provider/Settings\$Global;", + $$"Landroid/provider/Settings$Global;", "getInt", listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;", "I"), - "I" + "I", ) -private fun MethodReference.anyMethodSignatureMatches(vararg anyOf: MethodReference): Boolean { - return anyOf.any { - MethodUtil.methodSignaturesMatch(it, this) - } -} +private val getIntMethodReferences = listOf( + SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE, + SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE, +) @Suppress("unused") -val hideAdbStatusPatch = bytecodePatch( +val hideADBStatusPatch = bytecodePatch( name = "Hide ADB status", description = "Hides enabled development settings and/or ADB.", use = false, @@ -46,11 +45,8 @@ val hideAdbStatusPatch = bytecodePatch( val reference = instruction .takeIf { it.opcode == Opcode.INVOKE_STATIC } ?.getReference() - ?.takeIf { - it.anyMethodSignatureMatches( - SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE, - SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE - ) + ?.takeIf { reference -> + getIntMethodReferences.any { MethodUtil.methodSignaturesMatch(it, reference) } } ?: return@filterMap null @@ -67,9 +63,9 @@ val hideAdbStatusPatch = bytecodePatch( method.replaceInstruction( index, - "invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I" + "invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I", ) - } - ) + }, + ), ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/appicon/HideAppIconPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/appicon/HideAppIconPatch.kt index a0411d89f0..8e0faa6043 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/appicon/HideAppIconPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/appicon/HideAppIconPatch.kt @@ -3,8 +3,8 @@ package app.revanced.patches.all.misc.appicon import app.revanced.patcher.patch.resourcePatch import app.revanced.util.asSequence import app.revanced.util.childElementsSequence -import java.util.logging.Logger import org.w3c.dom.Element +import java.util.logging.Logger @Suppress("unused") val hideAppIconPatch = resourcePatch( @@ -12,7 +12,7 @@ val hideAppIconPatch = resourcePatch( description = "Hides the app icon from the Android launcher.", use = false, ) { - execute { + apply { document("AndroidManifest.xml").use { document -> var changed = false @@ -26,6 +26,7 @@ val hideAppIconPatch = resourcePatch( "action" -> if (child.getAttribute("android:name") == "android.intent.action.MAIN") { hasMainAction = true } + "category" -> if (child.getAttribute("android:name") == "android.intent.category.LAUNCHER") { launcherCategory = child } @@ -45,4 +46,3 @@ val hideAppIconPatch = resourcePatch( } } } - diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt index 434b97e276..567df9f7e0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.all.misc.build -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.util.getReference diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt index aa4fbf3f85..9e6e723692 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt @@ -11,172 +11,148 @@ val spoofBuildInfoPatch = bytecodePatch( use = false, ) { val board by stringOption( - key = "board", default = null, - title = "Board", + name = "Board", description = "The name of the underlying board, like \"goldfish\".", ) val bootloader by stringOption( - key = "bootloader", default = null, - title = "Bootloader", + name = "Bootloader", description = "The system bootloader version number.", ) val brand by stringOption( - key = "brand", default = null, - title = "Brand", + name = "Brand", description = "The consumer-visible brand with which the product/hardware will be associated, if any.", ) val cpuAbi by stringOption( - key = "cpu-abi", default = null, - title = "CPU ABI", + name = "CPU ABI", description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.", ) val cpuAbi2 by stringOption( - key = "cpu-abi-2", default = null, - title = "CPU ABI 2", + name = "CPU ABI 2", description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.", ) val device by stringOption( - key = "device", default = null, - title = "Device", + name = "Device", description = "The name of the industrial design.", ) val display by stringOption( - key = "display", default = null, - title = "Display", + name = "Display", description = "A build ID string meant for displaying to the user.", ) val fingerprint by stringOption( - key = "fingerprint", default = null, - title = "Fingerprint", + name = "Fingerprint", description = "A string that uniquely identifies this build.", ) val hardware by stringOption( - key = "hardware", default = null, - title = "Hardware", + name = "Hardware", description = "The name of the hardware (from the kernel command line or /proc).", ) val host by stringOption( - key = "host", default = null, - title = "Host", + name = "Host", description = "The host.", ) val id by stringOption( - key = "id", default = null, - title = "ID", + name = "ID", description = "Either a changelist number, or a label like \"M4-rc20\".", ) val manufacturer by stringOption( - key = "manufacturer", default = null, - title = "Manufacturer", + name = "Manufacturer", description = "The manufacturer of the product/hardware.", ) val model by stringOption( - key = "model", default = null, - title = "Model", + name = "Model", description = "The end-user-visible name for the end product.", ) val odmSku by stringOption( - key = "odm-sku", default = null, - title = "ODM SKU", + name = "ODM SKU", description = "The SKU of the device as set by the original design manufacturer (ODM).", ) val product by stringOption( - key = "product", default = null, - title = "Product", + name = "Product", description = "The name of the overall product.", ) val radio by stringOption( - key = "radio", default = null, - title = "Radio", + name = "Radio", description = "This field was deprecated in API level 15. " + "The radio firmware version is frequently not available when this class is initialized, " + "leading to a blank or \"unknown\" value for this string. Use getRadioVersion() instead.", ) val serial by stringOption( - key = "serial", default = null, - title = "Serial", + name = "Serial", description = "This field was deprecated in API level 26. Use getSerial() instead.", ) val sku by stringOption( - key = "sku", default = null, - title = "SKU", + name = "SKU", description = "The SKU of the hardware (from the kernel command line).", ) val socManufacturer by stringOption( - key = "soc-manufacturer", default = null, - title = "SOC manufacturer", + name = "SOC manufacturer", description = "The manufacturer of the device's primary system-on-chip.", ) val socModel by stringOption( - key = "soc-model", default = null, - title = "SOC model", + name = "SOC model", description = "The model name of the device's primary system-on-chip.", ) val tags by stringOption( - key = "tags", default = null, - title = "Tags", + name = "Tags", description = "Comma-separated tags describing the build, like \"unsigned,debug\".", ) val time by longOption( - key = "time", default = null, - title = "Time", + name = "Time", description = "The time at which the build was produced, given in milliseconds since the UNIX epoch.", ) val type by stringOption( - key = "type", default = null, - title = "Type", + name = "Type", description = "The type of build, like \"user\" or \"eng\".", ) val user by stringOption( - key = "user", default = null, - title = "User", + name = "User", description = "The user.", ) @@ -209,6 +185,5 @@ val spoofBuildInfoPatch = bytecodePatch( user, ) }, - ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt index b17eea94dc..8c69bfad07 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt @@ -1,8 +1,6 @@ -@file:Suppress("unused") - package app.revanced.patches.all.misc.connectivity.location.hide -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.IMethodCall import app.revanced.patches.all.misc.transformation.fromMethodReference diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt deleted file mode 100644 index fe418b1ae1..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.revanced.patches.all.misc.connectivity.telephony.sim.spoof - -import app.revanced.patcher.patch.bytecodePatch - -@Deprecated("Patch was renamed", ReplaceWith("spoofSimProviderPatch")) -@Suppress("unused") -val spoofSimCountryPatch = bytecodePatch { - dependsOn(spoofSimProviderPatch) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt index a1ae2c48d7..a7dfdf7bf4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimProviderPatch.kt @@ -1,21 +1,21 @@ package app.revanced.patches.all.misc.connectivity.telephony.sim.spoof -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.intOption import app.revanced.patcher.patch.stringOption -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.all.misc.transformation.transformInstructionsPatch 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.immutable.reference.ImmutableMethodReference import com.android.tools.smali.dexlib2.util.MethodUtil -import java.util.Locale +import java.util.* @Suppress("unused") -val spoofSimProviderPatch = bytecodePatch( +val spoofSIMProviderPatch = bytecodePatch( name = "Spoof SIM provider", description = "Spoofs information about the SIM card provider.", use = false, @@ -23,13 +23,11 @@ val spoofSimProviderPatch = bytecodePatch( val countries = Locale.getISOCountries().associateBy { Locale("", it).displayCountry } fun isoCountryPatchOption( - key: String, - title: String, + name: String, ) = stringOption( - key, + name, null, countries, - title, "ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code.", false, validator = { it: String? -> it == null || it.uppercase() in countries.values }, @@ -37,39 +35,29 @@ val spoofSimProviderPatch = bytecodePatch( fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999) - val networkCountryIso by isoCountryPatchOption( - "networkCountryIso", - "Network ISO country code", - ) + val networkCountryIso by isoCountryPatchOption("Network ISO country code") val networkOperator by intOption( - key = "networkOperator", - title = "MCC+MNC network operator code", + name = "MCC+MNC network operator code", description = "The 5 or 6 digits MCC+MNC (Mobile Country Code + Mobile Network Code) of the network operator.", - validator = { isMccMncValid(it) } + validator = { isMccMncValid(it) }, ) val networkOperatorName by stringOption( - key = "networkOperatorName", - title = "Network operator name", + name = "Network operator name", description = "The full name of the network operator.", ) - val simCountryIso by isoCountryPatchOption( - "simCountryIso", - "SIM ISO country code", - ) + val simCountryIso by isoCountryPatchOption("SIM ISO country code") val simOperator by intOption( - key = "simOperator", - title = "MCC+MNC SIM operator code", + name = "MCC+MNC SIM operator code", description = "The 5 or 6 digits MCC+MNC (Mobile Country Code + Mobile Network Code) of the SIM operator.", - validator = { isMccMncValid(it) } + validator = { isMccMncValid(it) }, ) val simOperatorName by stringOption( - key = "simOperatorName", - title = "SIM operator name", + name = "SIM operator name", description = "The full name of the SIM operator.", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt index 7abf5a4734..1666a27a8d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt @@ -11,7 +11,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" @Suppress("unused") -val spoofWifiPatch = bytecodePatch( +val spoofWiFiConnectionPatch = bytecodePatch( name = "Spoof Wi-Fi connection", description = "Spoofs an existing Wi-Fi connection.", use = false, diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/customcertificates/CustomCertificatesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/customcertificates/CustomCertificatesPatch.kt index 0097589776..661ee306b4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/customcertificates/CustomCertificatesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/customcertificates/CustomCertificatesPatch.kt @@ -9,34 +9,29 @@ import app.revanced.util.getNode import org.w3c.dom.Element import java.io.File - - - +@Suppress("unused") val customNetworkSecurityPatch = resourcePatch( name = "Custom network security", description = "Allows trusting custom certificate authorities for a specific domain.", - use = false + use = false, ) { val targetDomains by stringsOption( - key = "targetDomains", - title = "Target domains", + name = "Target domains", description = "List of domains to which the custom trust configuration will be applied (one domain per entry).", default = listOf("example.com"), - required = true + required = true, ) val includeSubdomains by booleanOption( - key = "includeSubdomains", - title = "Include subdomains", + name = "Include subdomains", description = "Applies the configuration to all subdomains of the target domains.", default = false, - required = true + required = true, ) val customCAFilePaths by stringsOption( - key = "customCAFilePaths", - title = "Custom CA file paths", + name = "Custom CA file paths", description = """ List of paths to files in PEM or DER format (one file path per entry). @@ -47,43 +42,35 @@ val customNetworkSecurityPatch = resourcePatch( CA files will be bundled in res/raw/ of resulting APK """.trimIndentMultiline(), default = null, - required = false + required = false, ) val allowUserCerts by booleanOption( - key = "allowUserCerts", - title = "Trust user added CAs", + name = "Trust user added CAs", description = "Makes an app trust certificates from the Android user store for the specified domains, and if the option \"Include Subdomains\" is enabled then also the subdomains.", - default = false, - required = true + required = true, ) val allowSystemCerts by booleanOption( - key = "allowSystemCerts", - title = "Trust system CAs", + name = "Trust system CAs", description = "Makes an app trust certificates from the Android system store for the specified domains, and and if the option \"Include Subdomains\" is enabled then also the subdomains.", - default = true, - required = true + required = true, ) val allowCleartextTraffic by booleanOption( - key = "allowCleartextTraffic", - title = "Allow cleartext traffic (HTTP)", + name = "Allow cleartext traffic (HTTP)", description = "Allows unencrypted HTTP traffic for the specified domains, and if \"Include Subdomains\" is enabled then also the subdomains.", - default = false, - required = true + required = true, ) val overridePins by booleanOption( - key = "overridePins", - title = "Override certificate pinning", + name = "Override certificate pinning", description = "Overrides certificate pinning for the specified domains and their subdomains if the option \"Include Subdomains\" is enabled to allow inspecting app traffic via a proxy.", - default = false, - required = true + required = true, ) fun generateNetworkSecurityConfig(): String { @@ -128,55 +115,47 @@ ${trustAnchorsXML.trimEnd()} - """.trimIndent() + """.trimIndent() } - - execute { + apply { val nscFileNameBare = "network_security_config" val resXmlDir = "res/xml" val resRawDir = "res/raw" val nscFileNameWithSuffix = "$nscFileNameBare.xml" - document("AndroidManifest.xml").use { document -> val applicationNode = document.getNode("application") as Element applicationNode.setAttribute("android:networkSecurityConfig", "@xml/$nscFileNameBare") } - File(get(resXmlDir), nscFileNameWithSuffix).apply { writeText(generateNetworkSecurityConfig()) } - - for (customCAFilePath in customCAFilePaths ?: emptyList()) { val file = File(customCAFilePath) if (!file.exists()) { throw PatchException( "The custom CA file path cannot be found: " + - file.absolutePath + file.absolutePath, ) } if (!file.isFile) { throw PatchException( - "The custom CA file path must be a file: " - + file.absolutePath + "The custom CA file path must be a file: " + + file.absolutePath, ) } val caFileNameWithoutSuffix = customCAFilePath.substringAfterLast('/').substringBefore('.') val caFile = File(customCAFilePath) File( get(resRawDir), - caFileNameWithoutSuffix + caFileNameWithoutSuffix, ).writeText( - caFile.readText() + caFile.readText(), ) - } - - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt index fc7e33c5dd..86e981a63a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt @@ -3,12 +3,13 @@ package app.revanced.patches.all.misc.debugging import app.revanced.patcher.patch.resourcePatch import org.w3c.dom.Element +@Suppress("unused") val enableAndroidDebuggingPatch = resourcePatch( name = "Enable Android debugging", description = "Enables Android debugging capabilities. This can slow down the app.", use = false, ) { - execute { + apply { document("AndroidManifest.xml").use { document -> val applicationNode = document 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/directory/documentsprovider/ExportInternalDataDocumentsProviderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/documentsprovider/ExportInternalDataDocumentsProviderPatch.kt index 187fd23818..46fd56daeb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/documentsprovider/ExportInternalDataDocumentsProviderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/documentsprovider/ExportInternalDataDocumentsProviderPatch.kt @@ -18,7 +18,7 @@ val exportInternalDataDocumentsProviderPatch = resourcePatch( }, ) - execute { + apply { val documentsProviderClass = "app.revanced.extension.all.misc.directory.documentsprovider.InternalDataDocumentsProvider" @@ -28,7 +28,7 @@ val exportInternalDataDocumentsProviderPatch = resourcePatch( .asSequence() .any { it.attributes.getNamedItem("android:name")?.nodeValue == documentsProviderClass } ) { - return@execute + return@apply } val authority = diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt index 9a9e427ece..74b21e50a2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt @@ -3,19 +3,16 @@ package app.revanced.patches.all.misc.hex import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.rawResourcePatch import app.revanced.patcher.patch.stringsOption -import app.revanced.patches.shared.misc.hex.HexPatchBuilder import app.revanced.patches.shared.misc.hex.hexPatch import app.revanced.util.Utils.trimIndentMultiline @Suppress("unused") -val hexPatch = rawResourcePatch( - name = "Hex", +val Hex = rawResourcePatch( description = "Replaces a hexadecimal patterns of bytes of files in an APK.", use = false, ) { val replacements by stringsOption( - key = "replacements", - title = "Replacements", + name = "Replacements", description = """ Hexadecimal patterns to search for and replace with another in a target file. @@ -34,10 +31,14 @@ val hexPatch = rawResourcePatch( dependsOn( hexPatch( - block = fun HexPatchBuilder.() { + block = { replacements!!.forEach { replacement -> try { - val (pattern, replacementPattern, targetFilePath) = replacement.split("|", limit = 3) + val (pattern, replacementPattern, targetFilePath) = replacement.split( + "|", + limit = 3 + ) + pattern asPatternTo replacementPattern inFile targetFilePath } catch (e: Exception) { throw PatchException( @@ -49,6 +50,6 @@ val hexPatch = rawResourcePatch( } } }, - ) + ), ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt index 29f644435d..ad96c61d8e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt @@ -8,7 +8,7 @@ val predictiveBackGesturePatch = resourcePatch( description = "Enables the predictive back gesture introduced on Android 13.", use = false, ) { - execute { + apply { val flag = "android:enableOnBackInvokedCallback" document("AndroidManifest.xml").use { document -> diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt index c85f4c3ba4..545e4ff90c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt @@ -3,6 +3,7 @@ package app.revanced.patches.all.misc.network import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch import app.revanced.util.Utils.trimIndentMultiline +import app.revanced.util.getNode import org.w3c.dom.Element import java.io.File @@ -14,16 +15,20 @@ val overrideCertificatePinningPatch = resourcePatch( ) { dependsOn(enableAndroidDebuggingPatch) - execute { + apply { val resXmlDirectory = get("res/xml") // Add android:networkSecurityConfig="@xml/network_security_config" and the "networkSecurityConfig" attribute if it does not exist. document("AndroidManifest.xml").use { document -> - val applicationNode = document.getElementsByTagName("application").item(0) as Element - - if (!applicationNode.hasAttribute("networkSecurityConfig")) { - document.createAttribute("android:networkSecurityConfig") - .apply { value = "@xml/network_security_config" }.let(applicationNode.attributes::setNamedItem) + val applicationNode = document.getNode("application") as Element + applicationNode.apply { + if (!hasAttribute("networkSecurityConfig")) { + attributes.setNamedItem( + document.createAttribute("android:networkSecurityConfig").apply { + value = "@xml/network_security_config" + } + ) + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt index 4a6443a8c9..271b2fc0fe 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt @@ -6,7 +6,15 @@ import app.revanced.util.getNode import org.w3c.dom.Element import java.util.logging.Logger -lateinit var packageNameOption: Option +private val packageNameOption = stringOption( + default = "Default", + values = mapOf("Default" to "Default"), + name = "Package name", + description = "The name of the package to rename the app to.", + required = true, +) { + it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) +} /** * Set the package name to use. @@ -26,40 +34,30 @@ fun setOrGetFallbackPackageName(fallbackPackageName: String): String { } } +@Suppress("ObjectPropertyName") val changePackageNamePatch = resourcePatch( name = "Change package name", description = "Appends \".revanced\" to the package name by default. " + "Changing the package name of the app can lead to unexpected issues.", use = false, ) { - packageNameOption = stringOption( - key = "packageName", - default = "Default", - values = mapOf("Default" to "Default"), - title = "Package name", - description = "The name of the package to rename the app to.", - required = true, - ) { - it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) - } + packageNameOption() val updatePermissions by booleanOption( - key = "updatePermissions", default = false, - title = "Update permissions", + name = "Update permissions", description = "Update compatibility receiver permissions. " + "Enabling this can fix installation errors, but this can also break features in certain apps.", ) val updateProviders by booleanOption( - key = "updateProviders", default = false, - title = "Update providers", + name = "Update providers", description = "Update provider names declared by the app. " + "Enabling this can fix installation errors, but this can also break features in certain apps.", ) - finalize { + afterDependents { /** * Apps that are confirmed to not work correctly with this patch. * This is not an exhaustive list, and is only the apps with @@ -80,7 +78,7 @@ val changePackageNamePatch = resourcePatch( val packageName = manifest.getAttribute("package") if (incompatibleAppPackages.contains(packageName)) { - return@finalize Logger.getLogger(this::class.java.name).severe( + return@afterDependents Logger.getLogger(this::class.java.name).severe( "'$packageName' does not work correctly with \"Change package name\"", ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt index 25a948e349..12461fc40a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt @@ -1,10 +1,9 @@ package app.revanced.patches.all.misc.playintegrity -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.util.getReference -import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference @@ -16,10 +15,9 @@ private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference( "Landroid/content/Context;", "bindService", listOf("Landroid/content/Intent;", "Landroid/content/ServiceConnection;", "I"), - "Z" + "Z", ) - @Suppress("unused") val disablePlayIntegrityPatch = bytecodePatch( name = "Disable Play Integrity", @@ -43,13 +41,14 @@ val disablePlayIntegrityPatch = bytecodePatch( transform = { method, entry -> val (instruction, index, parameterTypes) = entry val parameterString = parameterTypes.joinToString(separator = "") - val registerString = "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}, v${instruction.registerF}" + val registerString = + "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}, v${instruction.registerF}" method.replaceInstruction( index, - "invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->bindService(Landroid/content/Context;$parameterString)Z" + "invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->bindService(Landroid/content/Context;$parameterString)Z", ) - } - ) + }, + ), ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt index f32986ea6a..a77dd364fc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt @@ -215,8 +215,8 @@ fun addResources( * @see addResourcesPatch */ fun addResources( - patch: Patch<*>, - parseIds: (Patch<*>) -> Map> = { + patch: Patch, + parseIds: (Patch) -> Map> = { val patchId = patch.name ?: throw PatchException("Patch has no name") val packages = patch.compatiblePackages ?: throw PatchException("Patch has no compatible packages") @@ -273,9 +273,9 @@ val addResourcesPatch = resourcePatch( from the temporary map to addResourcesPatch. After all patches that depend on addResourcesPatch have been executed, - addResourcesPatch#finalize is finally called to add all staged resources to the app. + addResourcesPatch#afterDependents is finally called to add all staged resources to the app. */ - execute { + apply { stagedResources = buildMap { /** * Puts resources under `/resources/addresources//.xml` into the map. @@ -324,7 +324,7 @@ val addResourcesPatch = resourcePatch( // Stage all resources to a temporary map. // Staged resources consumed by addResourcesPatch#invoke(Patch) - // are later used in addResourcesPatch#finalize. + // are later used in addResourcesPatch#afterDependents. try { val addStringResources = { source: Value, dest: Value -> addResources(source, dest, "strings", StringResource::fromNode) @@ -343,7 +343,7 @@ val addResourcesPatch = resourcePatch( * Adds all resources staged in [addResourcesPatch] to the app. * This is called after all patches that depend on [addResourcesPatch] have been executed. */ - finalize { + afterDependents { operator fun MutableMap>.invoke( value: Value, resource: BaseResource, @@ -359,14 +359,14 @@ val addResourcesPatch = resourcePatch( } getOrPut(resourceFileName) { - this@finalize["res/$value/$resourceFileName.xml"].also { + this@afterDependents["res/$value/$resourceFileName.xml"].also { it.parentFile?.mkdirs() if (it.createNewFile()) { it.writeText("\n\n") } } - + document("res/$value/$resourceFileName.xml").let { document -> // Save the target node here as well diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt index f11d27f53c..0b0edcb69b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt @@ -10,7 +10,7 @@ import org.w3c.dom.Element private val removeCaptureRestrictionResourcePatch = resourcePatch( description = "Sets allowAudioPlaybackCapture in manifest to true.", ) { - execute { + apply { document("AndroidManifest.xml").use { document -> // Get the application node. val applicationNode = diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/PreventScreenshotDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/PreventScreenshotDetectionPatch.kt index b66753e2c0..93092b5f3e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/PreventScreenshotDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/PreventScreenshotDetectionPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.all.misc.screenshot -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.extensions.removeInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.util.getReference @@ -16,7 +16,7 @@ private val registerScreenCaptureCallbackMethodReference = ImmutableMethodRefere "Ljava/util/concurrent/Executor;", "Landroid/app/Activity\$ScreenCaptureCallback;", ), - "V" + "V", ) private val unregisterScreenCaptureCallbackMethodReference = ImmutableMethodReference( @@ -25,28 +25,30 @@ private val unregisterScreenCaptureCallbackMethodReference = ImmutableMethodRefe listOf( "Landroid/app/Activity\$ScreenCaptureCallback;", ), - "V" + "V", ) @Suppress("unused") val preventScreenshotDetectionPatch = bytecodePatch( name = "Prevent screenshot detection", description = "Removes the registration of all screen capture callbacks. This prevents the app from detecting screenshots.", - use = false + use = false, ) { - dependsOn(transformInstructionsPatch( - filterMap = { _, _, instruction, instructionIndex -> - if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@transformInstructionsPatch null - - val reference = instruction.getReference() ?: return@transformInstructionsPatch null + dependsOn( + transformInstructionsPatch( + filterMap = { _, _, instruction, instructionIndex -> + if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@transformInstructionsPatch null - instructionIndex.takeIf { - MethodUtil.methodSignaturesMatch(reference, registerScreenCaptureCallbackMethodReference) || - MethodUtil.methodSignaturesMatch(reference, unregisterScreenCaptureCallbackMethodReference) - } - }, - transform = { mutableMethod, instructionIndex -> - mutableMethod.removeInstruction(instructionIndex) - } - )) + val reference = instruction.getReference() ?: return@transformInstructionsPatch null + + instructionIndex.takeIf { + MethodUtil.methodSignaturesMatch(reference, registerScreenCaptureCallbackMethodReference) || + MethodUtil.methodSignaturesMatch(reference, unregisterScreenCaptureCallbackMethodReference) + } + }, + transform = { mutableMethod, instructionIndex -> + mutableMethod.removeInstruction(instructionIndex) + }, + ), + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt index 807afb6393..e54c3e59b1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.all.misc.screenshot -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.IMethodCall import app.revanced.patches.all.misc.transformation.filterMapInstruction35c diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt index 817a291971..a14e16a760 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt @@ -13,12 +13,13 @@ val removeShareTargetsPatch = resourcePatch( description = "Removes share targets like directly sharing to a frequent contact.", use = false, ) { - execute { + apply { try { document("res/xml/shortcuts.xml") } catch (_: FileNotFoundException) { - return@execute Logger.getLogger(this::class.java.name).warning( - "The app has no shortcuts. No changes applied.") + return@apply Logger.getLogger(this::class.java.name).warning( + "The app has no shortcuts. No changes applied.", + ) }.use { document -> val rootNode = document.getNode("shortcuts") as? Element ?: return@use diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofing.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofing.kt index ea91363121..99d7a2c3cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofing.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofing.kt @@ -5,7 +5,6 @@ import app.revanced.patcher.patch.stringOption import app.revanced.util.getNode import com.android.apksig.ApkVerifier import com.android.apksig.apk.ApkFormatException -import org.w3c.dom.Element import java.io.File import java.io.IOException import java.nio.file.InvalidPathException @@ -15,15 +14,14 @@ import java.security.cert.CertificateFactory import java.util.* @Suppress("unused") -val enableRomSignatureSpoofing = resourcePatch( +val enableROMSignatureSpoofingPatch = resourcePatch( name = "Enable ROM signature spoofing", description = "Spoofs the signature via the manifest meta-data \"fake-signature\". " + - "This patch only works with ROMs that support signature spoofing.", + "This patch only works with ROMs that support signature spoofing.", use = false, ) { val signatureOrPath by stringOption( - key = "signatureOrApkFilePath", - title = "Signature or APK file path", + name = "Signature or APK file path", validator = validator@{ signature -> signature ?: return@validator false @@ -32,14 +30,13 @@ val enableRomSignatureSpoofing = resourcePatch( description = "The hex-encoded signature or path to an APK file with the desired signature.", required = true, ) - execute { + apply { document("AndroidManifest.xml").use { document -> val permission = document.createElement("uses-permission").apply { setAttribute("android:name", "android.permission.FAKE_PACKAGE_SIGNATURE") } val manifest = document.getNode("manifest").appendChild(permission) - val fakeSignatureMetadata = document.createElement("meta-data").apply { setAttribute("android:name", "fake-signature") setAttribute("android:value", parseSignature(signatureOrPath!!)) @@ -70,15 +67,17 @@ private fun parseSignature(optionValue: String): String? { val hexFormat = HexFormat.of() - val signature = (if (result.isVerifiedUsingV3Scheme) { - result.v3SchemeSigners[0].certificate - } else if (result.isVerifiedUsingV2Scheme) { - result.v2SchemeSigners[0].certificate - } else if (result.isVerifiedUsingV1Scheme) { - result.v1SchemeSigners[0].certificate - } else { - return null - }).encoded + val signature = ( + if (result.isVerifiedUsingV3Scheme) { + result.v3SchemeSigners[0].certificate + } else if (result.isVerifiedUsingV2Scheme) { + result.v2SchemeSigners[0].certificate + } else if (result.isVerifiedUsingV1Scheme) { + result.v1SchemeSigners[0].certificate + } else { + return null + } + ).encoded return hexFormat.formatHex(signature) } catch (_: IOException) { @@ -89,4 +88,4 @@ private fun parseSignature(optionValue: String): String? { } return null -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34.kt index c79654d1fc..05beaf8f0c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34.kt @@ -6,13 +6,13 @@ import org.w3c.dom.Element import java.util.logging.Logger @Suppress("unused") -val setTargetSdkVersion34 = resourcePatch( +val setTargetSDKVersion34Patch = resourcePatch( name = "Set target SDK version 34", description = "Changes the target SDK to version 34 (Android 14). " + - "For devices running Android 15+, this will disable edge-to-edge display.", + "For devices running Android 15+, this will disable edge-to-edge display.", use = false, ) { - execute { + apply { val targetSdkOverride = 34 // Android 14. document("AndroidManifest.xml").use { document -> @@ -24,12 +24,12 @@ val setTargetSdkVersion34 = resourcePatch( try { val manifestElement = document.getNode("manifest") as Element val compileSdkVersion = Integer.parseInt( - manifestElement.getAttribute("android:compileSdkVersion") + manifestElement.getAttribute("android:compileSdkVersion"), ) if (compileSdkVersion <= targetSdkOverride) { getLogger().warning( "This app does not appear to use a target SDK above $targetSdkOverride: " + - "(compileSdkVersion: $compileSdkVersion)" + "(compileSdkVersion: $compileSdkVersion)", ) } } catch (_: Exception) { diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt index 0c39436d1c..0535d6f06f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt @@ -1,7 +1,7 @@ package app.revanced.patches.all.misc.transformation -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.replaceInstruction import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.iface.instruction.Instruction 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..914d8ab85e 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 @@ -1,49 +1,22 @@ package app.revanced.patches.all.misc.transformation +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.util.findMutableMethodOf +import app.revanced.util.forEachInstructionAsSequence import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.Instruction +@Deprecated( + "Use forEachInstructionAsSequence directly within a bytecodePatch", ReplaceWith( + "bytecodePatch { apply { forEachInstructionAsSequence(filterMap, transform) } }", + "app.revanced.util.forEachInstructionAsSequence", + "app.revanced.patcher.patch.bytecodePatch", + ) +) fun transformInstructionsPatch( filterMap: (ClassDef, Method, Instruction, Int) -> T?, transform: (MutableMethod, T) -> Unit, ) = bytecodePatch { - // Returns the patch indices as a Sequence, which will execute lazily. - fun findPatchIndices(classDef: ClassDef, method: Method): Sequence? = - method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) -> - filterMap(classDef, method, instruction, index) - } - - execute { - // Find all methods to patch - buildMap { - classes.forEach { classDef -> - val methods = buildList { - classDef.methods.forEach { method -> - // Since the Sequence executes lazily, - // using any() results in only calling - // filterMap until the first index has been found. - if (findPatchIndices(classDef, method)?.any() == true) add(method) - } - } - - if (methods.isNotEmpty()) { - put(classDef, methods) - } - } - }.forEach { (classDef, methods) -> - // And finally transform the methods... - val mutableClass = proxy(classDef).mutableClass - - methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod -> - val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque()) - ?: return@methods - - while (!patchIndices.isEmpty()) transform(mutableMethod, patchIndices.removeLast()) - } - } - } + apply { forEachInstructionAsSequence(filterMap, transform) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt index a50fe58da3..1aa803e222 100644 --- a/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt @@ -9,23 +9,22 @@ import org.w3c.dom.Element val changeVersionCodePatch = resourcePatch( name = "Change version code", description = "Changes the version code of the app. This will turn off app store updates " + - "and allows downgrading an existing app install to an older app version.", + "and allows downgrading an existing app install to an older app version.", use = false, ) { val versionCode by intOption( - key = "versionCode", default = Int.MAX_VALUE, values = mapOf( "Lowest" to 1, "Highest" to Int.MAX_VALUE, ), - title = "Version code", + name = "Version code", description = "The version code to use. Using the highest value turns off app store " + - "updates and allows downgrading an existing app install to an older app version.", + "updates and allows downgrading an existing app install to an older app version.", required = true, ) { versionCode -> versionCode!! >= 1 } - execute { + apply { document("AndroidManifest.xml").use { document -> val manifestElement = document.getNode("manifest") as Element manifestElement.setAttribute("android:versionCode", "$versionCode") diff --git a/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt index bf53d86220..1a8041d7dd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt @@ -1,22 +1,16 @@ package app.revanced.patches.amazon -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val deepLinkingPatch = bytecodePatch( +val alwaysAllowDeepLinkingPatch = bytecodePatch( name = "Always allow deep-linking", description = "Open Amazon links, even if the app is not set to handle Amazon links.", ) { compatibleWith("com.amazon.mShop.android.shopping") - execute { - deepLinkingFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + deepLinkingMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt index 1339149f43..2eebbc58fe 100644 --- a/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt @@ -1,11 +1,17 @@ package app.revanced.patches.amazon -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val deepLinkingFingerprint = fingerprint { +internal val BytecodePatchContext.deepLinkingMethod by gettingFirstMethodDeclaratively( + "https://www.", + "android.intent.action.VIEW" +) { accessFlags(AccessFlags.PRIVATE) - returns("Z") - parameters("L") - strings("https://www.", "android.intent.action.VIEW") + returnType("Z") + parameterTypes("L") } diff --git a/patches/src/main/kotlin/app/revanced/patches/angulus/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/angulus/ads/Fingerprints.kt index 3196adb7e1..9119bbdfbb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/angulus/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/angulus/ads/Fingerprints.kt @@ -1,6 +1,9 @@ package app.revanced.patches.angulus.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags // Keywords to search for in case the method name changes: @@ -9,10 +12,9 @@ import com.android.tools.smali.dexlib2.AccessFlags // dailyAdResetCount // MeasurementPrefs -// This fingerprint targets a method that returns the daily measurement count. +// This targets a method that returns the daily measurement count. // This method is used to determine if the user has reached the daily limit of measurements. -internal val getDailyMeasurementCountFingerprint = fingerprint { +internal val BytecodePatchContext.getDailyMeasurementCountMethod by gettingFirstMethodDeclaratively("dailyMeasurementCount") { accessFlags(AccessFlags.PRIVATE) - returns("I") - strings("dailyMeasurementCount") + returnType("I") } diff --git a/patches/src/main/kotlin/app/revanced/patches/angulus/ads/RemoveAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/angulus/ads/RemoveAdsPatch.kt index c1f3acb8ea..18af561cc5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/angulus/ads/RemoveAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/angulus/ads/RemoveAdsPatch.kt @@ -1,17 +1,17 @@ package app.revanced.patches.angulus.ads import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.misc.pairip.license.disableLicenseCheckPatch +import app.revanced.patches.shared.misc.pairip.license.disablePairipLicenseCheckPatch import app.revanced.util.returnEarly @Suppress("unused") -val angulusPatch = bytecodePatch(name = "Hide ads") { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.drinkplusplus.angulus") - dependsOn(disableLicenseCheckPatch) + dependsOn(disablePairipLicenseCheckPatch) - execute { + apply { // Always return 0 as the daily measurement count. - getDailyMeasurementCountFingerprint.method.returnEarly(0) + getDailyMeasurementCountMethod.returnEarly(0) } } 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/bandcamp/limitations/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt index 15d306bc94..8a7eaa096e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt @@ -1,7 +1,9 @@ package app.revanced.patches.bandcamp.limitations -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext -internal val handlePlaybackLimitsFingerprint = fingerprint { - strings("track_id", "play_count") -} +internal val BytecodePatchContext.handlePlaybackLimitsMethod by gettingFirstMethodDeclaratively( + "track_id", + "play_count" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt index f0740d04c3..6fda82ae46 100644 --- a/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt @@ -10,7 +10,7 @@ val removePlayLimitsPatch = bytecodePatch( ) { compatibleWith("com.bandcamp.android") - execute { - handlePlaybackLimitsFingerprint.method.returnEarly() + apply { + handlePlaybackLimitsMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt index c2abcc5b6f..fc8a3322ba 100644 --- a/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.cieid.restrictions.root -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") val bypassRootChecksPatch = bytecodePatch( @@ -10,7 +10,7 @@ val bypassRootChecksPatch = bytecodePatch( ) { compatibleWith("it.ipzs.cieid") - execute { - checkRootFingerprint.method.addInstruction(1, "return-void") + apply { + checkRootMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt index 387b0a0be8..8bbcb5ea2c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.cieid.restrictions.root -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val checkRootFingerprint = fingerprint { - custom { method, _ -> - method.name == "onResume" && method.definingClass == "Lit/ipzs/cieid/BaseActivity;" - } +internal val BytecodePatchContext.checkRootMethod by gettingFirstMethodDeclaratively { + name("onResume") + definingClass("Lit/ipzs/cieid/BaseActivity;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/Fingerprints.kt index bf2ee4120c..91df4531c9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/Fingerprints.kt @@ -1,28 +1,25 @@ package app.revanced.patches.com.sbs.ondemand.tv -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val shouldShowAdvertisingTVFingerprint = fingerprint { - returns("Z") - custom { method, classDef -> - method.name == "getShouldShowAdvertisingTV" && - classDef.type == "Lcom/sbs/ondemand/common/InMemoryStorage;" - } +val BytecodePatchContext.shouldShowAdvertisingTVMethod by gettingFirstMethodDeclaratively { + name("getShouldShowAdvertisingTV") + definingClass("Lcom/sbs/ondemand/common/InMemoryStorage;") + returnType("Z") } -internal val shouldShowPauseAdFingerprint = fingerprint { - returns("Z") - custom { method, classDef -> - method.name == "shouldShowPauseAd" && - classDef.type == "Lcom/sbs/ondemand/player/viewmodels/PauseAdController;" - } +internal val BytecodePatchContext.shouldShowPauseAdMethod by gettingFirstMethodDeclaratively { + name("shouldShowPauseAd") + definingClass("Lcom/sbs/ondemand/player/viewmodels/PauseAdController;") + returnType("Z") } -internal val requestAdStreamFingerprint = fingerprint { - returns("V") - custom { method, classDef -> - method.name == "requestAdStream\$player_googleStoreTvRelease" && - classDef.type == "Lcom/sbs/ondemand/player/viewmodels/AdsController;" - } +internal val BytecodePatchContext.requestAdStreamMethod by gettingFirstMethodDeclaratively { + name("requestAdStream\$player_googleStoreTvRelease") + definingClass("Lcom/sbs/ondemand/player/viewmodels/AdsController;") + returnType("V") } - diff --git a/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/RemoveAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/RemoveAdsPatch.kt index 1b628562a2..a0579b97ca 100644 --- a/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/RemoveAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/com/sbs/ondemand/tv/RemoveAdsPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.com.sbs.ondemand.tv -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.misc.pairip.license.disableLicenseCheckPatch +import app.revanced.patches.shared.misc.pairip.license.disablePairipLicenseCheckPatch import app.revanced.util.returnEarly @Suppress("unused") @@ -12,11 +12,11 @@ val removeAdsPatch = bytecodePatch( ) { compatibleWith("com.sbs.ondemand.tv") - dependsOn(disableLicenseCheckPatch) + dependsOn(disablePairipLicenseCheckPatch) - execute { - shouldShowAdvertisingTVFingerprint.method.returnEarly(true) - shouldShowPauseAdFingerprint.method.returnEarly(false) + apply { + shouldShowAdvertisingTVMethod.returnEarly(true) + shouldShowPauseAdMethod.returnEarly(false) // Remove on-demand pre-roll advertisements using exception handling. // Exception handling is used instead of returnEarly() because: @@ -24,14 +24,14 @@ val removeAdsPatch = bytecodePatch( // 2. SBS app has built-in exception handling in handleProviderFailure(). // 3. Exception triggers fallbackToAkamaiProvider() which loads actual content. // 4. This preserves the intended app flow: first try ads, then fail gracefully, then load content. - requestAdStreamFingerprint.method.addInstructions( - 0, + requestAdStreamMethod.addInstructions( + 0, """ new-instance v0, Ljava/lang/RuntimeException; const-string v1, "Ad stream disabled" invoke-direct {v0, v1}, Ljava/lang/RuntimeException;->(Ljava/lang/String;)V throw v0 - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/DisableAdsPatch.kt index 3a1829dbc8..6dde6e7257 100644 --- a/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/DisableAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/DisableAdsPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.cricbuzz.ads -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.cricbuzz.misc.extension.sharedExtensionPatch import app.revanced.util.getReference @@ -15,26 +15,25 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/cricbuzz/ads/HideAdsPatch;" @Suppress("unused") -val disableAdsPatch = bytecodePatch ( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.cricbuzz.android"("6.24.01")) dependsOn(sharedExtensionPatch) - execute { - userStateSwitchFingerprint.method.returnEarly(true) + apply { + userStateSwitchMethod.returnEarly(true) // Remove region-specific Cricbuzz11 elements. - cb11ConstructorFingerprint.method.addInstruction(0, "const/4 p7, 0x0") - getBottomBarFingerprint.method.apply { - val getIndex = indexOfFirstInstructionOrThrow() { + cb11ConstructorMethod.addInstruction(0, "const/4 p7, 0x0") + getBottomBarMethod.apply { + val getIndex = indexOfFirstInstructionOrThrow { opcode == Opcode.IGET_OBJECT && getReference()?.name == "bottomBar" } val getRegister = getInstruction(getIndex).registerA - addInstruction(getIndex + 1, - "invoke-static { v$getRegister }, $EXTENSION_CLASS_DESCRIPTOR->filterCb11(Ljava/util/List;)V" + addInstruction( + getIndex + 1, + "invoke-static { v$getRegister }, $EXTENSION_CLASS_DESCRIPTOR->filterCb11(Ljava/util/List;)V", ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/Fingerprints.kt index 9dd6844fda..cafcb7792b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/cricbuzz/ads/Fingerprints.kt @@ -1,15 +1,16 @@ package app.revanced.patches.cricbuzz.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val userStateSwitchFingerprint = fingerprint { +internal val BytecodePatchContext.userStateSwitchMethod by gettingFirstMethodDeclaratively("key.user.state", "NA") { opcodes(Opcode.SPARSE_SWITCH) - strings("key.user.state", "NA") } -internal val cb11ConstructorFingerprint = fingerprint { - parameters( +internal val BytecodePatchContext.cb11ConstructorMethod by gettingFirstMethodDeclaratively { + definingClass("CB11Details;") + parameterTypes( "Ljava/lang/String;", "Ljava/lang/String;", "Ljava/lang/String;", @@ -19,15 +20,11 @@ internal val cb11ConstructorFingerprint = fingerprint { "Z", "Ljava/lang/String;", "Ljava/lang/String;", - "L" + "L", ) - custom { _, classDef -> - classDef.endsWith("CB11Details;") - } } -internal val getBottomBarFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getBottomBar" && classDef.endsWith("HomeMenu;") - } -} \ No newline at end of file +internal val BytecodePatchContext.getBottomBarMethod by gettingFirstMethodDeclaratively { + name("getBottomBar") + definingClass("HomeMenu;") +} 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/crunchyroll/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/crunchyroll/ads/Fingerprints.kt index 0266e0344d..43bd86dbdb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/crunchyroll/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/crunchyroll/ads/Fingerprints.kt @@ -1,7 +1,10 @@ package app.revanced.patches.crunchyroll.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext -internal val videoUrlReadyToStringFingerprint = fingerprint { - strings("VideoUrlReady(url=", ", enableAds=") +internal val BytecodePatchContext.videoUrlReadyToStringMethodMatch by composingFirstMethod { + instructions("VideoUrlReady(url="(), ", enableAds="()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/crunchyroll/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/crunchyroll/ads/HideAdsPatch.kt index bb354a5686..3a697c3b75 100644 --- a/patches/src/main/kotlin/app/revanced/patches/crunchyroll/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/crunchyroll/ads/HideAdsPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.crunchyroll.ads -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.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction @@ -13,34 +13,35 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads" -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.crunchyroll.crunchyroid") - execute { + apply { // Get obfuscated "enableAds" field from toString method. - val enableAdsField = videoUrlReadyToStringFingerprint.let { - val strIndex = videoUrlReadyToStringFingerprint.stringMatches!!.last().index - val fieldIndex = it.method.indexOfFirstInstruction(strIndex, Opcode.IGET_BOOLEAN) - it.method.getInstruction(fieldIndex).getReference()!! + val enableAdsField = videoUrlReadyToStringMethodMatch.method.let { + val stringIndex = videoUrlReadyToStringMethodMatch[-1] + val fieldIndex = it.indexOfFirstInstruction(stringIndex, Opcode.IGET_BOOLEAN) + + it.getInstruction(fieldIndex).getReference()!! } // Remove final access flag on field. - videoUrlReadyToStringFingerprint.classDef.fields + videoUrlReadyToStringMethodMatch.classDef.fields .first { it.name == enableAdsField.name } .removeFlags(AccessFlags.FINAL) // Override enableAds field in non-default constructor. - val constructor = videoUrlReadyToStringFingerprint.classDef.methods.first { + val constructor = videoUrlReadyToStringMethodMatch.classDef.methods.first { AccessFlags.CONSTRUCTOR.isSet(it.accessFlags) && it.parameters.isNotEmpty() } + constructor.addInstructions( constructor.instructions.count() - 1, """ move-object/from16 v0, p0 const/4 v1, 0x0 iput-boolean v1, v0, $enableAdsField - """) + """, + ) } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/disneyplus/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/disneyplus/Fingerprints.kt index 5cd67cba86..cffcd34830 100644 --- a/patches/src/main/kotlin/app/revanced/patches/disneyplus/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/disneyplus/Fingerprints.kt @@ -1,19 +1,19 @@ -package app.revanced.patches.disneyplus.ads +package app.revanced.patches.disneyplus -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val insertionGetPointsFingerprint = fingerprint { - returns("Ljava/util/List") - custom { method, _ -> - method.name == "getPoints" && - method.definingClass == "Lcom/dss/sdk/internal/media/Insertion;" - } +internal val BytecodePatchContext.insertionGetPointsMethod by gettingFirstMethodDeclaratively { + name("getPoints") + definingClass("Lcom/dss/sdk/internal/media/Insertion;") + returnType("Ljava/util/List") } -internal val insertionGetRangesFingerprint = fingerprint { - returns("Ljava/util/List") - custom { method, _ -> - method.name == "getRanges" && - method.definingClass == "Lcom/dss/sdk/internal/media/Insertion;" - } +internal val BytecodePatchContext.insertionGetRangesMethod by gettingFirstMethodDeclaratively { + name("getRanges") + definingClass("Lcom/dss/sdk/internal/media/Insertion;") + returnType("Ljava/util/List") } diff --git a/patches/src/main/kotlin/app/revanced/patches/disneyplus/SkipAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/disneyplus/SkipAdsPatch.kt index 5b0f551cdb..b182241ab0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/disneyplus/SkipAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/disneyplus/SkipAdsPatch.kt @@ -1,6 +1,6 @@ -package app.revanced.patches.disneyplus.ads +package app.revanced.patches.disneyplus -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,11 +10,11 @@ val skipAdsPatch = bytecodePatch( ) { compatibleWith("com.disney.disneyplus") - execute { - arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach { - it.method.addInstructions( - 0, - """ + apply { + arrayOf(insertionGetPointsMethod, insertionGetRangesMethod).forEach { + it.addInstructions( + 0, + """ new-instance v0, Ljava/util/ArrayList; invoke-direct {v0}, Ljava/util/ArrayList;->()V return-object v0 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..b83834c67a 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 @@ -1,18 +1,18 @@ package app.revanced.patches.duolingo.ad -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @Suppress("unused") -val disableAdsPatch = bytecodePatch( - "Disable ads", -) { +val disableAdsPatch = bytecodePatch("Disable ads") { // 6.55.3 and higher can show ads after each exercise. compatibleWith("com.duolingo"("6.54.5")) - execute { + apply { // Couple approaches to remove ads exist: // // MonetizationDebugSettings has a boolean value for "disableAds". @@ -20,10 +20,9 @@ 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 + monetizationDebugSettingsToStringMethod.immutableClassDef.initializeMonetizationDebugSettingsMethodMatch.let { + it.method.apply { + val insertIndex = it[0] val register = getInstruction(insertIndex).registerA addInstructions( @@ -31,5 +30,6 @@ val disableAdsPatch = bytecodePatch( "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..ba0ffcc5c7 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 @@ -1,17 +1,19 @@ package app.revanced.patches.duolingo.ad -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val initializeMonetizationDebugSettingsFingerprint = fingerprint { +internal val ClassDef.initializeMonetizationDebugSettingsMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - // Parameters have not been reliable for fingerprinting between versions. + returnType("V") + // Parameters have not been reliable for matching between versions. opcodes(Opcode.IPUT_BOOLEAN) } -internal val monetizationDebugSettingsToStringFingerprint = fingerprint { - strings("MonetizationDebugSettings(") // Partial string match. - custom { method, _ -> method.name == "toString" } -} \ No newline at end of file +internal val BytecodePatchContext.monetizationDebugSettingsToStringMethod by gettingFirstMethodDeclaratively { + name("toString") + instructions(string("MonetizationDebugSettings(", String::contains)) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt index a507352e83..0d9681e8f8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt @@ -1,7 +1,9 @@ package app.revanced.patches.duolingo.debug -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -9,27 +11,20 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Suppress("unused") val enableDebugMenuPatch = bytecodePatch( name = "Enable debug menu", - use = false + use = false, ) { compatibleWith("com.duolingo") - execute { + apply { // It seems all categories are allowed on release. Force this on anyway. - debugCategoryAllowOnReleaseBuildsFingerprint.method.returnEarly(true) + debugCategoryAllowOnReleaseBuildsMethod.returnEarly(true) // Change build config debug build flag. - buildConfigProviderConstructorFingerprint.match( - buildConfigProviderToStringFingerprint.classDef - ).let { - val index = it.patternMatch!!.startIndex + buildConfigProviderToStringMethod.immutableClassDef.buildConfigProviderConstructorMethodMatch.let { + val index = it[0] - it.method.apply { - val register = getInstruction(index).registerA - addInstruction( - index + 1, - "const/4 v$register, 0x1" - ) - } + val register = it.method.getInstruction(index).registerA + it.method.addInstruction(index + 1, "const/4 v$register, 0x1") } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt index c3d663c838..5c030afb23 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt @@ -1,28 +1,27 @@ package app.revanced.patches.duolingo.debug -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val debugCategoryAllowOnReleaseBuildsFingerprint = fingerprint { - returns("Z") - parameters() - custom { method, classDef -> - method.name == "getAllowOnReleaseBuilds" && classDef.type == "Lcom/duolingo/debug/DebugCategory;" - } +internal val BytecodePatchContext.debugCategoryAllowOnReleaseBuildsMethod by gettingFirstMethodDeclaratively { + name("getAllowOnReleaseBuilds") + definingClass("Lcom/duolingo/debug/DebugCategory;") + returnType("Z") + parameterTypes() } -internal val buildConfigProviderConstructorFingerprint = fingerprint { +internal val ClassDef.buildConfigProviderConstructorMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters() + parameterTypes() opcodes(Opcode.CONST_4) } -internal val buildConfigProviderToStringFingerprint = fingerprint { - parameters() - returns("Ljava/lang/String;") - strings("BuildConfigProvider(") // Partial string match. - custom { method, _ -> - method.name == "toString" - } +internal val BytecodePatchContext.buildConfigProviderToStringMethod by gettingFirstMethodDeclaratively { + name("toString") + parameterTypes() + returnType("Ljava/lang/String;") + instructions(string("BuildConfigProvider(", String::contains)) } diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/Fingerprints.kt index 73dbcf304c..a0fa96ca64 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/Fingerprints.kt @@ -1,21 +1,25 @@ package app.revanced.patches.duolingo.energy -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef -/** - * Matches the class found in [energyConfigToStringFingerprint]. - */ -internal val initializeEnergyConfigFingerprint = fingerprint { +internal val ClassDef.initializeEnergyConfigMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) opcodes(Opcode.RETURN_VOID) } -// Class name currently is not obfuscated but it may be in the future. -internal val energyConfigToStringFingerprint = fingerprint { - parameters() - returns("Ljava/lang/String;") - strings("EnergyConfig(", "maxEnergy=") // Partial string matches. - custom { method, _ -> method.name == "toString" } +// Class name currently is not obfuscated, but it may be in the future. +internal val BytecodePatchContext.energyConfigToStringMethod by gettingFirstMethodDeclaratively { + name("toString") + parameterTypes() + returnType("Ljava/lang/String;") + instructions( + predicates=unorderedAllOf( + "EnergyConfig("(String::contains), + "maxEnergy="(String::contains), + ) + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/SkipEnergyRechargeAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/SkipEnergyRechargeAdsPatch.kt index bec800f50c..cb7be55660 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/SkipEnergyRechargeAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/energy/SkipEnergyRechargeAdsPatch.kt @@ -1,31 +1,32 @@ package app.revanced.patches.duolingo.energy -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.findFieldFromToString @Suppress("unused") val skipEnergyRechargeAdsPatch = bytecodePatch( name = "Skip energy recharge ads", - description = "Skips watching ads to recharge energy." + description = "Skips watching ads to recharge energy.", ) { compatibleWith("com.duolingo") - execute { - initializeEnergyConfigFingerprint - .match(energyConfigToStringFingerprint.classDef) - .method.apply { - val energyField = energyConfigToStringFingerprint.method - .findFieldFromToString("energy=") - val insertIndex = initializeEnergyConfigFingerprint.patternMatch!!.startIndex + apply { + energyConfigToStringMethod.immutableClassDef.initializeEnergyConfigMethodMatch.let { + it.method.apply { + val energyField = energyConfigToStringMethod.findFieldFromToString("energy=") + val insertIndex = it[0] addInstructions( insertIndex, """ - const/16 v0, 99 - iput v0, p0, $energyField - """ + const/16 v0, 99 + iput v0, p0, $energyField + """, ) } + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt index e84a448baa..9a06e21840 100644 --- a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt @@ -1,13 +1,14 @@ package app.revanced.patches.facebook.ads.mainfeed -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val baseModelMapperFingerprint = fingerprint { +internal val BytecodePatchContext.baseModelMapperMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Lcom/facebook/graphql/modelutil/BaseModelWithTree;") - parameters("Ljava/lang/Class", "I", "I") + returnType("Lcom/facebook/graphql/modelutil/BaseModelWithTree;") + parameterTypes("Ljava/lang/Class", "I", "I") opcodes( Opcode.SGET_OBJECT, Opcode.INVOKE_STATIC, @@ -16,11 +17,11 @@ internal val baseModelMapperFingerprint = fingerprint { Opcode.IF_EQ, ) } - -internal val getSponsoredDataModelTemplateFingerprint = fingerprint { +internal val BytecodePatchContext.getSponsoredDataModelTemplateMethod by gettingFirstImmutableMethodDeclaratively { + definingClass("Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() + returnType("L") + parameterTypes() opcodes( Opcode.CONST, Opcode.CONST, @@ -28,14 +29,10 @@ internal val getSponsoredDataModelTemplateFingerprint = fingerprint { Opcode.MOVE_RESULT_OBJECT, Opcode.RETURN_OBJECT, ) - custom { _, classDef -> - classDef.type == "Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;" - } } - -internal val getStoryVisibilityFingerprint = fingerprint { +internal val BytecodePatchContext.getStoryVisibilityMethodMatch by composingFirstMethod("This should not be called for base class object") { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Ljava/lang/String;") + returnType("Ljava/lang/String;") opcodes( Opcode.INSTANCE_OF, Opcode.IF_NEZ, @@ -45,5 +42,4 @@ internal val getStoryVisibilityFingerprint = fingerprint { Opcode.IF_NEZ, Opcode.CONST, ) - strings("This should not be called for base class object") } 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..8a2647eb52 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 @@ -1,9 +1,9 @@ package app.revanced.patches.facebook.ads.mainfeed -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i @@ -11,14 +11,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter @Suppress("unused") -val hideSponsoredStoriesPatch = bytecodePatch( - name = "Hide 'Sponsored Stories'", -) { +val hideSponsoredStoriesPatch = bytecodePatch("Hide 'Sponsored Stories'") { compatibleWith("com.facebook.katana"("490.0.0.63.82")) - execute { - val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateFingerprint.originalMethod - val baseModelMapperMethod = baseModelMapperFingerprint.originalMethod + apply { + val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateMethod val baseModelWithTreeType = baseModelMapperMethod.returnType val graphQlStoryClassDescriptor = "Lcom/facebook/graphql/model/GraphQLStory;" @@ -28,7 +25,7 @@ val hideSponsoredStoriesPatch = bytecodePatch( // could change in future version, we need to extract them and call the base implementation directly. val getSponsoredDataHelperMethod = ImmutableMethod( - getStoryVisibilityFingerprint.originalClassDef.type, + getStoryVisibilityMethodMatch.immutableClassDef.type, "getSponsoredData", listOf(ImmutableMethodParameter(graphQlStoryClassDescriptor, null, null)), baseModelWithTreeType, @@ -62,12 +59,12 @@ val hideSponsoredStoriesPatch = bytecodePatch( ) } - getStoryVisibilityFingerprint.classDef.methods.add(getSponsoredDataHelperMethod) + getStoryVisibilityMethodMatch.classDef.methods.add(getSponsoredDataHelperMethod) // 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, + getStoryVisibilityMethodMatch.method.addInstructionsWithLabels( + getStoryVisibilityMethodMatch[0], """ instance-of v0, p0, $graphQlStoryClassDescriptor if-eqz v0, :resume_normal diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt index 293d7ee82f..21cba5880b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt @@ -1,24 +1,24 @@ package app.revanced.patches.facebook.ads.story -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue -internal val adsInsertionFingerprint = fieldFingerprint( - fieldValue = "AdBucketDataSourceUtil\$attemptAdsInsertion\$1", +internal val BytecodePatchContext.adsInsertionMethod by runMethod( + fieldValue = $$"AdBucketDataSourceUtil$attemptAdsInsertion$1", ) -internal val fetchMoreAdsFingerprint = fieldFingerprint( - fieldValue = "AdBucketDataSourceUtil\$attemptFetchMoreAds\$1", +internal val BytecodePatchContext.fetchMoreAdsMethod by runMethod( + fieldValue = $$"AdBucketDataSourceUtil$attemptFetchMoreAds$1", ) -internal fun fieldFingerprint(fieldValue: String) = fingerprint { - returns("V") - parameters() - custom { method, classDef -> - method.name == "run" && - classDef.fields.any any@{ field -> - if (field.name != "__redex_internal_original_name") return@any false - (field.initialValue as? StringEncodedValue)?.value == fieldValue - } +internal fun runMethod(fieldValue: String) = gettingFirstMethodDeclaratively { + name("run") + returnType("V") + parameterTypes() + custom { + immutableClassDef.anyField { + name == "__redex_internal_original_name" && (initialValue as? StringEncodedValue)?.value == fieldValue + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt index ec811fc3c3..88e44efc79 100644 --- a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt @@ -1,7 +1,8 @@ package app.revanced.patches.facebook.ads.story -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") val hideStoryAdsPatch = bytecodePatch( @@ -10,12 +11,7 @@ val hideStoryAdsPatch = bytecodePatch( ) { compatibleWith("com.facebook.katana") - execute { - setOf( - fetchMoreAdsFingerprint, - adsInsertionFingerprint, - ).forEach { fingerprint -> - fingerprint.method.replaceInstruction(0, "return-void") - } + apply { + setOf(fetchMoreAdsMethod, adsInsertionMethod).forEach(MutableMethod::returnEarly) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt index 76dc2413fa..81507bb21c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt @@ -1,24 +1,18 @@ package app.revanced.patches.finanzonline.detection.bootloader -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val bootloaderDetectionPatch = bytecodePatch( +val removeBootloaderDetectionPatch = bytecodePatch( name = "Remove bootloader detection", description = "Removes the check for an unlocked bootloader.", ) { compatibleWith("at.gv.bmf.bmf2go") - execute { - setOf(createKeyFingerprint, bootStateFingerprint).forEach { fingerprint -> - fingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + setOf(createKeyMethod, bootStateMethod).forEach { method -> + method.returnEarly(true) } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt index 4c8ee1c541..30b1eb312e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt @@ -1,13 +1,17 @@ package app.revanced.patches.finanzonline.detection.bootloader -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode // Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#isBootStateOk (3.0.1) -internal val bootStateFingerprint = fingerprint { +internal val BytecodePatchContext.bootStateMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC) - returns("Z") + returnType("Z") opcodes( Opcode.INVOKE_DIRECT, Opcode.MOVE_RESULT_OBJECT, @@ -25,13 +29,18 @@ internal val bootStateFingerprint = fingerprint { Opcode.IF_NE, Opcode.GOTO, Opcode.MOVE, - Opcode.RETURN + Opcode.RETURN, ) } // Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#createKey (3.0.1) -internal val createKeyFingerprint = fingerprint { +internal val BytecodePatchContext.createKeyMethod by gettingFirstMethodDeclaratively( + "attestation", + "SHA-256", + "random", + "EC", + "AndroidKeyStore", +) { accessFlags(AccessFlags.PUBLIC) - returns("Z") - strings("attestation", "SHA-256", "random", "EC", "AndroidKeyStore") -} \ No newline at end of file + returnType("Z") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt index fec7f1249f..3e5fb33109 100644 --- a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt @@ -1,14 +1,15 @@ package app.revanced.patches.finanzonline.detection.root -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode // Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.RootDetection#isRooted (3.0.1) -internal val rootDetectionFingerprint = fingerprint { +internal val BytecodePatchContext.rootDetectionMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("L") - parameters("L") + returnType("L") + parameterTypes("L") opcodes( Opcode.NEW_INSTANCE, Opcode.INVOKE_DIRECT, diff --git a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt index c5ce1efe11..8ff3efd1c6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt @@ -1,19 +1,17 @@ package app.revanced.patches.finanzonline.detection.root -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION -import app.revanced.patches.shared.PATCH_NAME_REMOVE_ROOT_DETECTION @Suppress("unused") -val rootDetectionPatch = bytecodePatch( - name = PATCH_NAME_REMOVE_ROOT_DETECTION, - description = PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION, +val removeRootDetectionPatch = bytecodePatch( + name = "Remove root detection", + description = "Removes the check for root permissions and unlocked bootloader.", ) { compatibleWith("at.gv.bmf.bmf2go") - execute { - rootDetectionFingerprint.method.addInstructions( + apply { + rootDetectionMethod.addInstructions( 0, """ sget-object v0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean; diff --git a/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/Fingerprints.kt index e37f52ed9c..0a9b331615 100644 --- a/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/Fingerprints.kt @@ -1,12 +1,17 @@ package app.revanced.patches.fotmob.ads +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass import com.android.tools.smali.dexlib2.AccessFlags import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val shouldDisplayAdsMethod = fingerprint { +internal val BytecodePatchContext.shouldDisplayAdsMethod by gettingFirstMethodDeclaratively { + name("shouldDisplayAds") + definingClass("AdsService;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - custom { method, classDef -> - method.name == "shouldDisplayAds" && classDef.type.endsWith("AdsService;") - } + returnType("Z") } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/HideAdsPatch.kt index 563247fdd2..1b8910ba54 100644 --- a/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/fotmob/ads/HideAdsPatch.kt @@ -9,7 +9,7 @@ val hideAdsPatch = bytecodePatch( ) { compatibleWith("com.mobilefootie.wc2010") - execute { - shouldDisplayAdsMethod.method.returnEarly(false) + apply { + shouldDisplayAdsMethod.returnEarly(false) } -} \ No newline at end of file +} 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..a095f0d743 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 @@ -1,7 +1,7 @@ package app.revanced.patches.googlenews.customtabs -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -12,9 +12,9 @@ val enableCustomTabsPatch = bytecodePatch( ) { compatibleWith("com.google.android.apps.magazines") - execute { - launchCustomTabFingerprint.method.apply { - val checkIndex = launchCustomTabFingerprint.patternMatch!!.endIndex + 1 + apply { + launchCustomTabMethodMatch.method.apply { + val checkIndex = launchCustomTabMethodMatch[-1] + 1 val register = getInstruction(checkIndex).registerA replaceInstruction(checkIndex, "const/4 v$register, 0x1") diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt index 8880c010e9..4d27d45281 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt @@ -1,10 +1,15 @@ package app.revanced.patches.googlenews.customtabs -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.definingClass +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val launchCustomTabFingerprint = fingerprint { +internal val BytecodePatchContext.launchCustomTabMethodMatch by composingFirstMethod { + definingClass("CustomTabsArticleLauncher;") accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) opcodes( Opcode.IPUT_OBJECT, @@ -13,5 +18,4 @@ internal val launchCustomTabFingerprint = fingerprint { Opcode.CONST_4, Opcode.IPUT_BOOLEAN, ) - custom { _, classDef -> classDef.endsWith("CustomTabsArticleLauncher;") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt index 4e9e5c14c9..3096647525 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt @@ -1,5 +1,11 @@ package app.revanced.patches.googlenews.misc.extension.hooks +import app.revanced.patcher.definingClass +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.name +import app.revanced.patcher.opcodes import app.revanced.patches.shared.misc.extension.extensionHook import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow @@ -10,19 +16,20 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference private var getApplicationContextIndex = -1 internal val startActivityInitHook = extensionHook( - insertIndexResolver = { method -> - getApplicationContextIndex = method.indexOfFirstInstructionOrThrow { + getInsertIndex = { + getApplicationContextIndex = indexOfFirstInstructionOrThrow { getReference()?.name == "getApplicationContext" } getApplicationContextIndex + 2 // Below the move-result-object instruction. }, - contextRegisterResolver = { method -> - val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1) - as OneRegisterInstruction + getContextRegister = { + val moveResultInstruction = instructions.elementAt(getApplicationContextIndex + 1) as OneRegisterInstruction "v${moveResultInstruction.registerA}" }, ) { + name("onCreate") + definingClass("/StartActivity;") opcodes( Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT, @@ -35,7 +42,4 @@ internal val startActivityInitHook = extensionHook( Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext(). Opcode.MOVE_RESULT_OBJECT, ) - custom { methodDef, classDef -> - methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;") - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt index 6ddeb3e07f..8557f4f496 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.googlenews.misc.gms -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val magazinesActivityOnCreateFingerprint = fingerprint { - custom { methodDef, classDef -> - methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;") - } +internal val BytecodePatchContext.magazinesActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("/StartActivity;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt index 69481a8033..a795ed1f7c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt @@ -1,23 +1,23 @@ package app.revanced.patches.googlenews.misc.gms +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.Option import app.revanced.patches.googlenews.misc.extension.extensionPatch import app.revanced.patches.googlenews.misc.gms.Constants.MAGAZINES_PACKAGE_NAME import app.revanced.patches.googlenews.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch import app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch -import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow -import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Suppress("unused") val gmsCoreSupportPatch = gmsCoreSupportPatch( fromPackageName = MAGAZINES_PACKAGE_NAME, toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME, - mainActivityOnCreateFingerprintToInsertIndex = magazinesActivityOnCreateFingerprint to { + getMainActivityOnCreateMethodToGetInsertIndex = BytecodePatchContext::magazinesActivityOnCreateMethod::get to { val getApplicationContextIndex = - magazinesActivityOnCreateFingerprint.method.indexOfFirstInstructionOrThrow { - getReference()?.name == "getApplicationContext" + magazinesActivityOnCreateMethod.indexOfFirstInstructionOrThrow { + methodReference?.name == "getApplicationContext" } getApplicationContextIndex + 2 // Below the move-result-object instruction. diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/EnableDCIMFoldersBackupControlPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/EnableDCIMFoldersBackupControlPatch.kt index 35c541159e..bc13aaa1b4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/EnableDCIMFoldersBackupControlPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/EnableDCIMFoldersBackupControlPatch.kt @@ -7,12 +7,12 @@ import app.revanced.util.returnEarly val enableDCIMFoldersBackupControlPatch = bytecodePatch( name = "Enable DCIM folders backup control", description = "Disables always on backup for the Camera and other DCIM folders, allowing you to control backup " + - "for each folder individually. This will make the app default to having no folders backed up.", + "for each folder individually. This will make the app default to having no folders backed up.", use = false, ) { compatibleWith("com.google.android.apps.photos") - execute { - isDCIMFolderBackupControlDisabled.method.returnEarly(false) + apply { + isDCIMFolderBackupControlMethod.returnEarly(false) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/Fingerprints.kt index 71dda1f3ac..60dae79bc3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/backup/Fingerprints.kt @@ -1,8 +1,9 @@ package app.revanced.patches.googlephotos.misc.backup -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val isDCIMFolderBackupControlDisabled = fingerprint { - returns("Z") - strings("/dcim", "/mars_files/") +internal val BytecodePatchContext.isDCIMFolderBackupControlMethod by gettingFirstMethodDeclaratively("/dcim", "/mars_files/") { + returnType("Z") } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Hooks.kt index ca1065faaf..8ed9937cff 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Hooks.kt @@ -1,5 +1,11 @@ package app.revanced.patches.googlephotos.misc.extension +import app.revanced.patcher.definingClass +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.name +import app.revanced.patcher.opcodes import app.revanced.patches.shared.misc.extension.extensionHook import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow @@ -10,19 +16,20 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference private var getApplicationContextIndex = -1 internal val homeActivityInitHook = extensionHook( - insertIndexResolver = { method -> - getApplicationContextIndex = method.indexOfFirstInstructionOrThrow { + getInsertIndex = { + getApplicationContextIndex = indexOfFirstInstructionOrThrow { getReference()?.name == "getApplicationContext" } getApplicationContextIndex + 2 // Below the move-result-object instruction. }, - contextRegisterResolver = { method -> - val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1) - as OneRegisterInstruction + getContextRegister = { + val moveResultInstruction = instructions.elementAt(getApplicationContextIndex + 1) as OneRegisterInstruction "v${moveResultInstruction.registerA}" }, ) { + name("onCreate") + definingClass("/HomeActivity;") opcodes( Opcode.CONST_STRING, Opcode.INVOKE_STATIC, @@ -31,7 +38,4 @@ internal val homeActivityInitHook = extensionHook( Opcode.INVOKE_VIRTUAL, // Calls getApplicationContext(). Opcode.MOVE_RESULT_OBJECT, ) - custom { methodDef, classDef -> - methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;") - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt index 95f2a3dba7..bf61f98d57 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt @@ -1,7 +1,6 @@ package app.revanced.patches.googlephotos.misc.features -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext -internal val initializeFeaturesEnumFingerprint = fingerprint { - strings("com.google.android.apps.photos.NEXUS_PRELOAD") -} +internal val BytecodePatchContext.initializeFeaturesEnumMethod by gettingFirstMethodDeclaratively("com.google.android.apps.photos.NEXUS_PRELOAD") \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt index db6158d27f..71046d14eb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.googlephotos.misc.features -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.stringsOption import app.revanced.util.getReference @@ -19,18 +19,16 @@ val spoofFeaturesPatch = bytecodePatch( dependsOn(spoofBuildInfoPatch) val featuresToEnable by stringsOption( - key = "featuresToEnable", default = listOf( "com.google.android.apps.photos.NEXUS_PRELOAD", "com.google.android.apps.photos.nexus_preload", ), - title = "Features to enable", + name = "Features to enable", description = "Google Pixel exclusive features to enable. Features up to Pixel XL enable the unlimited storage feature.", required = true, ) val featuresToDisable by stringsOption( - key = "featuresToDisable", default = listOf( "com.google.android.apps.photos.PIXEL_2017_PRELOAD", "com.google.android.apps.photos.PIXEL_2018_PRELOAD", @@ -49,20 +47,20 @@ val spoofFeaturesPatch = bytecodePatch( "com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE", "com.google.android.feature.PIXEL_2025_EXPERIENCE", ), - title = "Features to disable", + name = "Features to disable", description = "Google Pixel exclusive features to disable." + "Features after Pixel XL may have to be disabled for unlimited storage depending on the device.", required = true, ) - execute { + apply { @Suppress("NAME_SHADOWING") val featuresToEnable = featuresToEnable!!.toSet() @Suppress("NAME_SHADOWING") val featuresToDisable = featuresToDisable!!.toSet() - initializeFeaturesEnumFingerprint.method.apply { + initializeFeaturesEnumMethod.apply { instructions.filter { it.opcode == Opcode.CONST_STRING }.forEach { val feature = it.getReference()!!.string diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt index f47c1a3d94..315c5677a4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.googlephotos.misc.gms -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val homeActivityOnCreateFingerprint = fingerprint { - custom { methodDef, classDef -> - methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;") - } +internal val BytecodePatchContext.homeActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("/HomeActivity;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt index 4a27e93cae..65ae7572c4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt @@ -1,5 +1,7 @@ package app.revanced.patches.googlephotos.misc.gms +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.Option import app.revanced.patches.googlephotos.misc.extension.extensionPatch import app.revanced.patches.googlephotos.misc.gms.Constants.PHOTOS_PACKAGE_NAME @@ -8,14 +10,16 @@ import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow @Suppress("unused") val gmsCoreSupportPatch = gmsCoreSupportPatch( fromPackageName = PHOTOS_PACKAGE_NAME, toPackageName = REVANCED_PHOTOS_PACKAGE_NAME, - mainActivityOnCreateFingerprintToInsertIndex = homeActivityOnCreateFingerprint to { - val index = homeActivityOnCreateFingerprint.method.indexOfFirstInstructionOrThrow { - getReference()?.name == "getApplicationContext" + getMainActivityOnCreateMethodToGetInsertIndex = BytecodePatchContext::homeActivityOnCreateMethod::get to { + val index = homeActivityOnCreateMethod.indexOfFirstInstructionOrThrow { + methodReference?.name == "getApplicationContext" } // Below the move-result-object instruction, 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/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt index 62e1e5f16b..6e9577a191 100644 --- a/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt @@ -1,12 +1,10 @@ package app.revanced.patches.googlerecorder.restrictions -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val onApplicationCreateFingerprint = fingerprint { - strings("com.google.android.feature.PIXEL_2017_EXPERIENCE") - custom { method, classDef -> - if (method.name != "onCreate") return@custom false - - classDef.endsWith("RecorderApplication;") - } +internal val BytecodePatchContext.onApplicationCreateMethodMatch by composingFirstMethod { + name("onCreate") + definingClass("RecorderApplication;") + instructions("com.google.android.feature.PIXEL_2017_EXPERIENCE"()) } 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..ee67ea3ce0 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 @@ -1,8 +1,8 @@ package app.revanced.patches.googlerecorder.restrictions -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.removeInstructions import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -13,10 +13,10 @@ val removeDeviceRestrictionsPatch = bytecodePatch( ) { compatibleWith("com.google.android.apps.recorder") - execute { - val featureStringIndex = onApplicationCreateFingerprint.stringMatches!!.first().index + apply { + val featureStringIndex = onApplicationCreateMethodMatch[0] - onApplicationCreateFingerprint.method.apply { + onApplicationCreateMethodMatch.method.apply { // Remove check for device restrictions. removeInstructions(featureStringIndex - 2, 5) diff --git a/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt index 1132a5ad97..870c2f5e32 100644 --- a/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt @@ -1,21 +1,13 @@ package app.revanced.patches.hexeditor.ad -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val disableAdsPatch = bytecodePatch( - name = "Disable ads", -) { +val disableAdsPatch = bytecodePatch("Disable ads") { compatibleWith("com.myprog.hexedit") - execute { - primaryAdsFingerprint.method.replaceInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + primaryAdsMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt index 2fa2c5b851..b62d33e39c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.hexeditor.ad -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val primaryAdsFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("PreferencesHelper;") && method.name == "isAdsDisabled" - } +internal val BytecodePatchContext.primaryAdsMethod by gettingFirstMethodDeclaratively { + name("isAdsDisabled") + definingClass("PreferencesHelper;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt index 84db554572..8305f2a059 100644 --- a/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt @@ -1,8 +1,11 @@ package app.revanced.patches.iconpackstudio.misc.pro -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val checkProFingerprint = fingerprint { - returns("Z") - custom { _, classDef -> classDef.endsWith("IPSPurchaseRepository;") } -} \ No newline at end of file +internal val BytecodePatchContext.checkProMethod by gettingFirstMethodDeclaratively { + definingClass("IPSPurchaseRepository;") + returnType("Z") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt index c1e4719b7a..f3325f70a3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt @@ -1,21 +1,13 @@ package app.revanced.patches.iconpackstudio.misc.pro -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val unlockProPatch = bytecodePatch( - name = "Unlock pro", -) { +val unlockProPatch = bytecodePatch("Unlock pro") { compatibleWith("ginlemon.iconpackstudio"("2.2 build 016")) - execute { - checkProFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + checkProMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/Fingerprints.kt index c8ebd86df4..1c73e1cb04 100644 --- a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/Fingerprints.kt @@ -1,22 +1,19 @@ package app.revanced.patches.idaustria.detection.deviceintegrity -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val isDeviceBootloaderOpenFingerprint = fingerprint { +internal val BytecodePatchContext.isDeviceBootloaderOpenMethod by gettingFirstMethodDeclaratively { + name("isDeviceBootloaderOpen") + definingClass("/DeviceIntegrityCheckProviderImpl;") accessFlags(AccessFlags.PUBLIC) - returns("Ljava/lang/Object;") - custom { method, classDef -> - method.name == "isDeviceBootloaderOpen" && - classDef.endsWith("/DeviceIntegrityCheckProviderImpl;") - } + returnType("Ljava/lang/Object;") } -internal val isDeviceRootedFingerprint = fingerprint { +internal val BytecodePatchContext.isDeviceRootedMethod by gettingFirstMethodDeclaratively { + name("isDeviceRooted") + definingClass("/DeviceIntegrityCheckProviderImpl;") accessFlags(AccessFlags.PUBLIC) - returns("Z") - custom { method, classDef -> - method.name == "isDeviceRooted" && - classDef.endsWith("/DeviceIntegrityCheckProviderImpl;") - } + returnType("Z") } diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/RemoveDeviceIntegrityChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/RemoveDeviceIntegrityChecksPatch.kt index 3c72e69f35..2b3b6b0109 100644 --- a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/RemoveDeviceIntegrityChecksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/deviceintegrity/RemoveDeviceIntegrityChecksPatch.kt @@ -1,10 +1,9 @@ package app.revanced.patches.idaustria.detection.deviceintegrity -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly - @Suppress("unused") val removeDeviceIntegrityChecksPatch = bytecodePatch( name = "Remove device integrity checks", @@ -12,19 +11,17 @@ val removeDeviceIntegrityChecksPatch = bytecodePatch( ) { compatibleWith("at.gv.oe.app") - execute { - isDeviceRootedFingerprint.method.returnEarly(false) + apply { + isDeviceRootedMethod.returnEarly(false) - isDeviceBootloaderOpenFingerprint.method.apply { - addInstructions( - 0, - """ - const/4 v0, 0x0 - invoke-static { v0 }, Lkotlin/coroutines/jvm/internal/Boxing;->boxBoolean(Z)Ljava/lang/Boolean; - move-result-object v0 - return-object v0 - """ - ) - } + isDeviceBootloaderOpenMethod.addInstructions( + 0, + """ + const/4 v0, 0x0 + invoke-static { v0 }, Lkotlin/coroutines/jvm/internal/Boxing;->boxBoolean(Z)Ljava/lang/Boolean; + move-result-object v0 + return-object v0 + """, + ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt deleted file mode 100644 index cfb7e8d686..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.patches.idaustria.detection.root - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.idaustria.detection.deviceintegrity.removeDeviceIntegrityChecksPatch - -@Deprecated("Patch was superseded", ReplaceWith("removeDeviceIntegrityChecksPatch")) -@Suppress("unused") -val rootDetectionPatch = bytecodePatch { - dependsOn(removeDeviceIntegrityChecksPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt index 61cd9605f1..0dc98775d1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt @@ -1,13 +1,13 @@ package app.revanced.patches.idaustria.detection.signature -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val spoofSignatureFingerprint = fingerprint { +internal val BytecodePatchContext.spoofSignatureMethod by gettingFirstMethodDeclaratively { + name("getPubKey") + definingClass("/SL2Step1Task;") accessFlags(AccessFlags.PRIVATE) - returns("L") - parameters("L") - custom { method, classDef -> - classDef.endsWith("/SL2Step1Task;") && method.name == "getPubKey" - } + returnType("L") + parameterTypes("L") } diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt index 3c4a939c12..f25f35ed8b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt @@ -10,7 +10,7 @@ val spoofSignaturePatch = bytecodePatch( ) { compatibleWith("at.gv.oe.app") - execute { + apply { val expectedSignature = "OpenSSLRSAPublicKey{modulus=ac3e6fd6050aa7e0d6010ae58190404cd89a56935b44f6fee" + "067c149768320026e10b24799a1339e414605e448e3f264444a327b9ae292be2b62ad567dd1800dbed4a88f718a33dc6db6b" + @@ -24,6 +24,6 @@ val spoofSignaturePatch = bytecodePatch( "77ef1be61b2c01ebdabddcbf53cc4b6fd9a3c445606ee77b3758162c80ad8f8137b3c6864e92db904807dcb2be9d7717dd21" + "bf42c121d620ddfb7914f7a95c713d9e1c1b7bdb4a03d618e40cf7e9e235c0b5687e03b7ab3,publicExponent=10001}" - spoofSignatureFingerprint.method.returnEarly(expectedSignature) + spoofSignatureMethod.returnEarly(expectedSignature) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt index 573bd72e35..60369a6c54 100644 --- a/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt @@ -1,8 +1,11 @@ package app.revanced.patches.inshorts.ad -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val inshortsAdsFingerprint = fingerprint { - returns("V") - strings("GoogleAdLoader", "exception in requestAd") +internal val BytecodePatchContext.inshortsAdsMethod by gettingFirstMethodDeclaratively( + "GoogleAdLoader", "exception in requestAd" +) { + returnType("V") } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt index 87fddcb78a..9665f4ace0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt @@ -1,20 +1,13 @@ package app.revanced.patches.inshorts.ad -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.nis.app") - execute { - inshortsAdsFingerprint.method.addInstruction( - 0, - """ - return-void - """, - ) + apply { + inshortsAdsMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt index e82a54b057..7e8e21492c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt @@ -1,16 +1,14 @@ package app.revanced.patches.instagram.ads import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.meta.ads.adInjectorFingerprint +import app.revanced.patches.meta.ads.adInjectorMethod import app.revanced.util.returnEarly @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.instagram.android") - execute { - adInjectorFingerprint.method.returnEarly(false) + apply { + adInjectorMethod.returnEarly(false) } } 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..cb6c056a8b 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,12 @@ package app.revanced.patches.instagram.feed -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively import app.revanced.patcher.patch.BytecodePatchContext -internal val mainFeedRequestClassFingerprint = fingerprint { - strings("Request{mReason=", ", mInstanceNumber=") -} +internal val BytecodePatchContext.mainFeedRequestClassMethod by gettingFirstMethodDeclaratively( + "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") -} +internal val BytecodePatchContext.mainFeedHeaderMapFinderMethod by gettingFirstMethodDeclaratively( + "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..c998611255 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 @@ -1,7 +1,12 @@ package app.revanced.patches.instagram.feed -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.fieldReference +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.immutableClassDef +import app.revanced.patcher.name import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch import app.revanced.util.getReference @@ -12,16 +17,16 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/instagram/feed/LimitFeedToFollowedProfiles;" @Suppress("unused") -val limitFeedToFollowedProfiles = bytecodePatch( +val limitFeedToFollowedProfilesPatch = bytecodePatch( name = "Limit feed to followed profiles", description = "Filters the home feed to display only content from profiles you follow.", - use = false + use = false, ) { compatibleWith("com.instagram.android") dependsOn(sharedExtensionPatch) - execute { + apply { /** * Since the header field is obfuscated and there is no easy way to identify it among all the class fields, * an additional method is fingerprinted. @@ -29,19 +34,19 @@ val limitFeedToFollowedProfiles = bytecodePatch( */ val mainFeedRequestHeaderFieldName: String - with(mainFeedHeaderMapFinderFingerprint.method) { + mainFeedHeaderMapFinderMethod.apply { mainFeedRequestHeaderFieldName = indexOfFirstInstructionOrThrow { - getReference().let { ref -> - ref?.type == "Ljava/util/Map;" && - ref.definingClass == mainFeedRequestClassFingerprint.classDef.toString() - - } + val reference = fieldReference + reference?.type == "Ljava/util/Map;" && + reference.definingClass == mainFeedRequestClassMethod.classDef.type }.let { instructionIndex -> getInstruction(instructionIndex).getReference()!!.name } } - initMainFeedRequestFingerprint.method.apply { + mainFeedRequestClassMethod.immutableClassDef.firstMethodDeclaratively { + name("") + }.apply { // Finds the instruction where the map is being initialized in the constructor val getHeaderIndex = indexOfFirstInstructionOrThrow { getReference().let { @@ -57,7 +62,7 @@ val limitFeedToFollowedProfiles = bytecodePatch( """ invoke-static { v$paramHeaderRegister }, $EXTENSION_CLASS_DESCRIPTOR->setFollowingHeader(Ljava/util/Map;)Ljava/util/Map; move-result-object v$paramHeaderRegister - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/AnonymousStoryViewingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/AnonymousStoryViewingPatch.kt index 72cae7fd2a..123ada7c2f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/AnonymousStoryViewingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/AnonymousStoryViewingPatch.kt @@ -12,12 +12,12 @@ val anonymousStoryViewingPatch = bytecodePatch( Your view will not appear in the story viewers list. Note: Since no data is sent, a story you have already viewed may appear as new on another device. """.trimIndentMultiline(), - use = false + use = false, ) { compatibleWith("com.instagram.android") - execute { + apply { // Prevent the hashmap of the seen media to be filled - setMediaSeenHashmapFingerprint.method.returnEarly() + setMediaSeenHashmapMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/Fingerprints.kt index 59cce2aac0..a7027aa61e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/ghost/story/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.instagram.ghost.story -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val setMediaSeenHashmapFingerprint = fingerprint { - parameters() - returns("V") - strings("media/seen/") +internal val BytecodePatchContext.setMediaSeenHashmapMethod by gettingFirstMethodDeclaratively("media/seen/") { + parameterTypes() + returnType("V") } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt index a85e8eb304..a9b705dd4e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt @@ -1,11 +1,12 @@ - package app.revanced.patches.instagram.hide.explore -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal const val EXPLORE_KEY_TO_BE_HIDDEN = "sectional_items" - -internal val exploreResponseJsonParserFingerprint = fingerprint { - strings(EXPLORE_KEY_TO_BE_HIDDEN, "ExploreTopicalFeedResponse") - custom { method, _ -> method.name == "parseFromJson" } +internal val BytecodePatchContext.exploreResponseJsonParserMethodMatch by composingFirstMethod("ExploreTopicalFeedResponse") { + name("parseFromJson") + instructions("sectional_items"()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt index f9ec6505f9..dbab25ba4f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt @@ -1,7 +1,9 @@ package app.revanced.patches.instagram.hide.explore +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.instagram.shared.replaceStringWithBogus +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Suppress("unused") val hideExploreFeedPatch = bytecodePatch( @@ -11,7 +13,12 @@ val hideExploreFeedPatch = bytecodePatch( ) { compatibleWith("com.instagram.android") - execute { - exploreResponseJsonParserFingerprint.replaceStringWithBogus(EXPLORE_KEY_TO_BE_HIDDEN) + apply { + exploreResponseJsonParserMethodMatch.method.apply { + val targetStringIndex = exploreResponseJsonParserMethodMatch[0] + val targetStringRegister = getInstruction(targetStringIndex).registerA + + replaceInstruction(targetStringIndex, "const-string v$targetStringRegister, \"BOGUS\"") + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/Fingerprints.kt index 9a0341ee69..871bd27df7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/Fingerprints.kt @@ -1,9 +1,12 @@ package app.revanced.patches.instagram.hide.highlightsTray -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext internal const val TARGET_STRING = "highlights_tray" -internal val highlightsUrlBuilderFingerprint = fingerprint { - strings(TARGET_STRING,"X-IG-Accept-Hint") +internal val BytecodePatchContext.highlightsUrlBuilderMethodMatch by composingFirstMethod("X-IG-Accept-Hint") { + instructions(TARGET_STRING()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/HideHighlightsTrayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/HideHighlightsTrayPatch.kt index 3b777d9ed5..804ac49d2e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/HideHighlightsTrayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/highlightsTray/HideHighlightsTrayPatch.kt @@ -1,17 +1,24 @@ package app.revanced.patches.instagram.hide.highlightsTray +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.instagram.shared.replaceStringWithBogus +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Suppress("unused") val hideHighlightsTrayPatch = bytecodePatch( name = "Hide highlights tray", description = "Hides the highlights tray in profile section.", - use = false + use = false, ) { compatibleWith("com.instagram.android") - execute { - highlightsUrlBuilderFingerprint.replaceStringWithBogus(TARGET_STRING) + apply { + highlightsUrlBuilderMethodMatch.method.apply { + val targetStringIndex = highlightsUrlBuilderMethodMatch[0] + val targetStringRegister = getInstruction(targetStringIndex).registerA + + replaceInstruction(targetStringIndex, "const-string v$targetStringRegister, \"BOGUS\"") + } } } 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..0a07351aa9 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 @@ -1,24 +1,18 @@ - package app.revanced.patches.instagram.hide.navigation -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val initializeNavigationButtonsListFingerprint = fingerprint { +internal val BytecodePatchContext.initializeNavigationButtonsListMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Lcom/instagram/common/session/UserSession;", "Z") - returns("Ljava/util/List;") + parameterTypes("Lcom/instagram/common/session/UserSession;", "Z") + returnType("Ljava/util/List;") } -private 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 - } -} +internal val BytecodePatchContext.navigationButtonsEnumMethod by gettingFirstImmutableMethodDeclaratively( + "FEED", + "fragment_feed", + "SEARCH", + "fragment_search", +) 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 b946abf6bc..ef4070c62a 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,10 +1,12 @@ package app.revanced.patches.instagram.hide.navigation -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.immutableClassDef +import app.revanced.patcher.name import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch -import app.revanced.patches.shared.PATCH_NAME_HIDE_NAVIGATION_BUTTONS import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findFreeRegister import app.revanced.util.getReference @@ -20,131 +22,123 @@ private const val EXTENSION_CLASS_DESCRIPTOR = @Suppress("unused") val hideNavigationButtonsPatch = bytecodePatch( - name = PATCH_NAME_HIDE_NAVIGATION_BUTTONS, + name = "Hide navigation buttons", description = "Hides navigation bar buttons, such as the Reels and Create button.", - use = false + use = false, ) { compatibleWith("com.instagram.android"("401.0.0.48.79")) dependsOn(sharedExtensionPatch) val hideHome by booleanOption( - key = "hideHome", default = false, - title = "Hide Home", - description = "Permanently hides the Home button. App starts at next available tab." // On the "homecoming" / current instagram layout. + name = "Hide Home", + description = "Permanently hides the Home button. App starts at next available tab.", // On the "homecoming" / current instagram layout. ) val hideReels by booleanOption( - key = "hideReels", default = true, - title = "Hide Reels", - description = "Permanently hides the Reels button." + name = "Hide Reels", + description = "Permanently hides the Reels button.", ) val hideDirect by booleanOption( - key = "hideDirect", default = false, - title = "Hide Direct", - description = "Permanently hides the Direct button." + name = "Hide Direct", + description = "Permanently hides the Direct button.", ) val hideSearch by booleanOption( - key = "hideSearch", default = false, - title = "Hide Search", - description = "Permanently hides the Search button." + name = "Hide Search", + description = "Permanently hides the Search button.", ) val hideProfile by booleanOption( - key = "hideProfile", default = false, - title = "Hide Profile", - description = "Permanently hides the Profile button." + name = "Hide Profile", + description = "Permanently hides the Profile button.", ) val hideCreate by booleanOption( - key = "hideCreate", default = true, - title = "Hide Create", - description = "Permanently hides the Create button." + name = "Hide Create", + description = "Permanently hides the Create button.", ) - execute { - if (!hideHome!! &&!hideReels!! && !hideDirect!! && !hideSearch!! && !hideProfile!! && !hideCreate!!) { - return@execute Logger.getLogger(this::class.java.name).warning( - "No hide navigation buttons options are enabled. No changes made." + apply { + if (!hideHome!! && !hideReels!! && !hideDirect!! && !hideSearch!! && !hideProfile!! && !hideCreate!!) { + return@apply Logger.getLogger(this::class.java.name).warning( + "No hide navigation buttons options are enabled. No changes made.", ) } - val enumNameField: String - - // Get the field name which contains the name of the enum for the navigation button ("fragment_clips", "fragment_share", ...) - with(navigationButtonsEnumInitFingerprint.method) { - enumNameField = indexOfFirstInstructionOrThrow { + // Get the field name which contains the name of the enum for the navigation button + // ("fragment_clips", "fragment_share", ...) + val enumNameField = navigationButtonsEnumMethod.immutableClassDef.firstMethodDeclaratively { + name("") + }.let { method -> + method.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 + method.getInstruction(it).getReference()!!.name } } - initializeNavigationButtonsListFingerprint.method.apply { + initializeNavigationButtonsListMethod.apply { val returnIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) val buttonsListRegister = getInstruction(returnIndex).registerA val freeRegister = findFreeRegister(returnIndex) val freeRegister2 = findFreeRegister(returnIndex, freeRegister) - fun instructionsRemoveButtonByName(buttonEnumName: String): String { - return """ + fun instructionsRemoveButtonByName(buttonEnumName: String): String = """ const-string v$freeRegister, "$buttonEnumName" const-string v$freeRegister2, "$enumNameField" invoke-static { v$buttonsListRegister, v$freeRegister, v$freeRegister2 }, $EXTENSION_CLASS_DESCRIPTOR->removeNavigationButtonByName(Ljava/util/List;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; move-result-object v$buttonsListRegister """ - } if (hideHome!!) { addInstructionsAtControlFlowLabel( returnIndex, - instructionsRemoveButtonByName("fragment_feed") + instructionsRemoveButtonByName("fragment_feed"), ) } if (hideReels!!) { addInstructionsAtControlFlowLabel( returnIndex, - instructionsRemoveButtonByName("fragment_clips") + instructionsRemoveButtonByName("fragment_clips"), ) } if (hideDirect!!) { addInstructionsAtControlFlowLabel( returnIndex, - instructionsRemoveButtonByName("fragment_direct_tab") + instructionsRemoveButtonByName("fragment_direct_tab"), ) } if (hideSearch!!) { addInstructionsAtControlFlowLabel( returnIndex, - instructionsRemoveButtonByName("fragment_search") + instructionsRemoveButtonByName("fragment_search"), ) } if (hideCreate!!) { addInstructionsAtControlFlowLabel( returnIndex, - instructionsRemoveButtonByName("fragment_share") + instructionsRemoveButtonByName("fragment_share"), ) } if (hideProfile!!) { addInstructionsAtControlFlowLabel( returnIndex, - instructionsRemoveButtonByName("fragment_profile") + instructionsRemoveButtonByName("fragment_profile"), ) } - } } } 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..3c9e118335 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 @@ -1,17 +1,16 @@ package app.revanced.patches.instagram.hide.stories -import app.revanced.patcher.fingerprint + +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode - -internal val getOrCreateAvatarViewFingerprint = fingerprint { - parameters() - returns("L") - custom { method, classDef -> - classDef.type == "Lcom/instagram/reels/ui/views/reelavatar/RecyclerReelAvatarView;" - } - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.IPUT_OBJECT, - Opcode.INVOKE_VIRTUAL // Add View (Story) - ) - } +internal val BytecodePatchContext.getOrCreateAvatarViewMethodMatch by composingFirstMethod { + definingClass("Lcom/instagram/reels/ui/views/reelavatar/RecyclerReelAvatarView;") + parameterTypes() + returnType("L") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.IPUT_OBJECT, + Opcode.INVOKE_VIRTUAL, // Add View (Story). + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/HideStories.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/HideStories.kt index 65c51cf36e..fed889d77b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/HideStories.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/stories/HideStories.kt @@ -1,20 +1,22 @@ package app.revanced.patches.instagram.hide.stories -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction + +import app.revanced.patcher.extensions.removeInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") -val hideStoriesPatch = bytecodePatch( +val hideStoriesFromHomePatch = bytecodePatch( name = "Hide Stories from Home", description = "Hides Stories from the main page, by removing the buttons.", - use = false + use = false, ) { compatibleWith("com.instagram.android") - execute { - val addStoryMethod = getOrCreateAvatarViewFingerprint.method // Creates Story - val addStoryEndIndex = getOrCreateAvatarViewFingerprint.patternMatch!!.endIndex + apply { + getOrCreateAvatarViewMethodMatch.let { + val addStoryEndIndex = it[-1] - // Remove addView of Story. - addStoryMethod.removeInstruction(addStoryEndIndex) + // Remove addView of Story. + it.method.removeInstruction(addStoryEndIndex) + } } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/Fingerprints.kt index 0f731b4f42..9d486f5520 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/Fingerprints.kt @@ -1,6 +1,10 @@ package app.revanced.patches.instagram.hide.suggestions -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.unorderedAllOf internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf( "clips_netego", @@ -12,6 +16,6 @@ internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf( "suggested_users", ) -internal val feedItemParseFromJsonFingerprint = fingerprint { - strings(*FEED_ITEM_KEYS_TO_BE_HIDDEN, "FeedItem") +internal val BytecodePatchContext.feedItemParseFromJsonMethodMatch by composingFirstMethod("FeedItem") { + instructions(predicates = unorderedAllOf(predicates = FEED_ITEM_KEYS_TO_BE_HIDDEN.map { it() }.toTypedArray())) } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContent.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContent.kt deleted file mode 100644 index 9dc6dbf593..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContent.kt +++ /dev/null @@ -1,19 +0,0 @@ -package app.revanced.patches.instagram.hide.suggestions - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.instagram.shared.replaceStringWithBogus - -@Suppress("unused") -val hideSuggestedContent = bytecodePatch( - name = "Hide suggested content", - description = "Hides suggested stories, reels, threads and survey from feed (Suggested posts will still be shown).", - use = false, -) { - compatibleWith("com.instagram.android") - - execute { - FEED_ITEM_KEYS_TO_BE_HIDDEN.forEach { key -> - feedItemParseFromJsonFingerprint.replaceStringWithBogus(key) - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContentPatch.kt new file mode 100644 index 0000000000..f82335e186 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContentPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.instagram.hide.suggestions + +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val hideSuggestedContentPatch = bytecodePatch( + name = "Hide suggested content", + description = "Hides suggested stories, reels, threads and survey from feed (Suggested posts will still be shown).", + use = false, +) { + compatibleWith("com.instagram.android") + + apply { + feedItemParseFromJsonMethodMatch.method.apply { + feedItemParseFromJsonMethodMatch.indices[0].forEach { targetStringIndex -> + val targetStringRegister = getInstruction(targetStringIndex).registerA + + replaceInstruction(targetStringIndex, "const-string v$targetStringRegister, \"BOGUS\"") + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/EnableDeveloperMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/EnableDeveloperMenuPatch.kt index e9577477ad..1d44ac8259 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/EnableDeveloperMenuPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/EnableDeveloperMenuPatch.kt @@ -1,12 +1,11 @@ package app.revanced.patches.instagram.misc.devmenu +import app.revanced.patcher.extensions.methodReference import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.Utils.trimIndentMultiline -import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Suppress("unused") val enableDeveloperMenuPatch = bytecodePatch( @@ -16,22 +15,23 @@ val enableDeveloperMenuPatch = bytecodePatch( It is recommended to use this patch with an alpha/beta Instagram release. Patching a stable release works, but the developer menu shows the developer flags as numbers and does not show a human readable description. """.trimIndentMultiline(), - use = false + use = false, ) { compatibleWith("com.instagram.android") - execute { - with(clearNotificationReceiverFingerprint.method) { - indexOfFirstInstructionReversedOrThrow(clearNotificationReceiverFingerprint.stringMatches!!.first().index) { - val reference = getReference() + apply { + clearNotificationReceiverMethodMatch.let { + val stringIndex = it[0] + + it.immutableMethod.indexOfFirstInstructionReversedOrThrow(stringIndex) { + val reference = methodReference opcode in listOf(Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC_RANGE) && - reference?.parameterTypes?.size == 1 && - reference.parameterTypes.first() == "Lcom/instagram/common/session/UserSession;" && - reference.returnType == "Z" + reference?.parameterTypes?.size == 1 && + reference.parameterTypes.first() == "Lcom/instagram/common/session/UserSession;" && + reference.returnType == "Z" }.let { index -> - navigate(this).to(index).stop().returnEarly(true) + navigate(it.immutableMethod).to(index).stop().returnEarly(true) } } } } - diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/Fingerprints.kt index 8b84606f00..13d455eff9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/devmenu/Fingerprints.kt @@ -1,12 +1,10 @@ - package app.revanced.patches.instagram.misc.devmenu -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val clearNotificationReceiverFingerprint = fingerprint { - custom { method, classDef -> - method.name == "onReceive" && - classDef.type == "Lcom/instagram/notifications/push/ClearNotificationReceiver;" - } - strings("NOTIFICATION_DISMISSED") +internal val BytecodePatchContext.clearNotificationReceiverMethodMatch by composingFirstMethod { + name("onReceive") + definingClass("Lcom/instagram/notifications/push/ClearNotificationReceiver;") + instructions("NOTIFICATION_DISMISSED"()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/DisableAnalyticsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/DisableAnalyticsPatch.kt index 819a927813..32d4870e8b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/DisableAnalyticsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/DisableAnalyticsPatch.kt @@ -1,8 +1,10 @@ package app.revanced.patches.instagram.misc.disableAnalytics -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.instagram.shared.replaceStringWithBogus +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Suppress("unused") val disableAnalyticsPatch = bytecodePatch( @@ -11,10 +13,10 @@ val disableAnalyticsPatch = bytecodePatch( ) { compatibleWith("com.instagram.android") - execute { + apply { // Returns BOGUS as analytics url. - instagramAnalyticsUrlBuilderMethodFingerprint.method.addInstructions( - 0, + instagramAnalyticsUrlBuilderMethod.addInstructions( + 0, """ const-string v0, "BOGUS" return-object v0 @@ -22,7 +24,12 @@ val disableAnalyticsPatch = bytecodePatch( ) // Replaces analytics url as BOGUS. - facebookAnalyticsUrlInitMethodFingerprint.replaceStringWithBogus(TARGET_URL) + facebookAnalyticsUrlInitMethodMatch.let { match -> + match.method.apply { + val urlIndex = match[1] + val register = getInstruction(urlIndex).registerA + replaceInstruction(urlIndex, "const-string v$register, \"BOGUS\"") + } + } } } - diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/Fingerprints.kt index 95e522f4c6..ba84c2edcb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/disableAnalytics/Fingerprints.kt @@ -1,12 +1,19 @@ package app.revanced.patches.instagram.misc.disableAnalytics -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.strings -internal val instagramAnalyticsUrlBuilderMethodFingerprint = fingerprint { +internal val BytecodePatchContext.instagramAnalyticsUrlBuilderMethod by gettingFirstMethodDeclaratively { strings("/logging_client_events") } -internal const val TARGET_URL = "https://graph.facebook.com/logging_client_events" -internal val facebookAnalyticsUrlInitMethodFingerprint = fingerprint { - strings("analytics_endpoint",TARGET_URL) +internal val BytecodePatchContext.facebookAnalyticsUrlInitMethodMatch by composingFirstMethod { + instructions( + "analytics_endpoint"(), + "https://graph.facebook.com/logging_client_events"() + ) } 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/links/Fingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/links/Fingerprint.kt index b33d5ace54..676a942410 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/links/Fingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/links/Fingerprint.kt @@ -1,9 +1,14 @@ package app.revanced.patches.instagram.misc.links -import app.revanced.patcher.fingerprint + +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType internal const val TARGET_STRING = "Tracking.ARG_CLICK_SOURCE" -internal val inAppBrowserFunctionFingerprint = fingerprint { - returns("Z") - strings("TrackingInfo.ARG_MODULE_NAME", TARGET_STRING) +internal val BytecodePatchContext.inAppBrowserFunctionMethodMatch by composingFirstMethod("TrackingInfo.ARG_MODULE_NAME") { + instructions(TARGET_STRING()) + returnType("Z") } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/links/OpenLinksExternallyPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/links/OpenLinksExternallyPatch.kt index 6dfbf9b73c..181b8683eb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/links/OpenLinksExternallyPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/links/OpenLinksExternallyPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.instagram.misc.links -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch import app.revanced.util.indexOfFirstInstructionOrThrow @@ -21,13 +21,14 @@ val openLinksExternallyPatch = bytecodePatch( compatibleWith("com.instagram.android") - execute { - inAppBrowserFunctionFingerprint.let { - val stringMatchIndex = it.stringMatches?.first { match -> match.string == TARGET_STRING }!!.index + apply { + inAppBrowserFunctionMethodMatch.let { + val stringMatchIndex = it[0] it.method.apply { val urlResultObjIndex = indexOfFirstInstructionOrThrow( - stringMatchIndex, Opcode.MOVE_OBJECT_FROM16 + stringMatchIndex, + Opcode.MOVE_OBJECT_FROM16, ) // Register that contains the url after moving from a higher register. @@ -39,7 +40,7 @@ val openLinksExternallyPatch = bytecodePatch( invoke-static/range { v$urlRegister .. v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->openExternally(Ljava/lang/String;)Z move-result v0 return v0 - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/Fingerprints.kt index ad4a66540c..5cbd918d9d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/Fingerprints.kt @@ -1,12 +1,15 @@ package app.revanced.patches.instagram.misc.removeBuildExpiredPopup -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext -internal const val MILLISECOND_IN_A_DAY_LITERAL = 0x5265c00L +private const val MILLISECOND_IN_A_DAY_LITERAL = 0x5265c00L -internal val appUpdateLockoutBuilderFingerprint = fingerprint { - strings("android.hardware.sensor.hinge_angle") - literal { MILLISECOND_IN_A_DAY_LITERAL } +internal val BytecodePatchContext.appUpdateLockoutBuilderMethod by gettingFirstMethodDeclaratively( + "android.hardware.sensor.hinge_angle", +) { + instructions(MILLISECOND_IN_A_DAY_LITERAL()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/RemoveBuildExpiredPopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/RemoveBuildExpiredPopupPatch.kt index 9d19e928ed..00bd45fa45 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/RemoveBuildExpiredPopupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/removeBuildExpiredPopup/RemoveBuildExpiredPopupPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.instagram.misc.removeBuildExpiredPopup -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -14,8 +14,8 @@ val removeBuildExpiredPopupPatch = bytecodePatch( ) { compatibleWith("com.instagram.android") - execute { - appUpdateLockoutBuilderFingerprint.method.apply { + apply { + appUpdateLockoutBuilderMethod.apply { val longToIntIndex = instructions.first { it.opcode == Opcode.LONG_TO_INT }.location.index val appAgeRegister = getInstruction(longToIntIndex).registerA @@ -24,4 +24,3 @@ val removeBuildExpiredPopupPatch = bytecodePatch( } } } - diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/EditShareLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/EditShareLinksPatch.kt index e3e4630101..f4397018b1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/EditShareLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/EditShareLinksPatch.kt @@ -1,26 +1,24 @@ package app.revanced.patches.instagram.misc.share -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.BytecodePatchContext -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction -context(BytecodePatchContext) -internal fun editShareLinksPatch(block: MutableMethod.(index: Int, register: Int) -> Unit) { - val fingerprintsToPatch = arrayOf( - permalinkResponseJsonParserFingerprint, - storyUrlResponseJsonParserFingerprint, - profileUrlResponseJsonParserFingerprint, - liveUrlResponseJsonParserFingerprint +internal fun BytecodePatchContext.editShareLinksPatch(block: MutableMethod.(index: Int, register: Int) -> Unit) { + val methodsToPatch = arrayOf( + permalinkResponseJsonParserMethodMatch, + storyUrlResponseJsonParserMethodMatch, + profileUrlResponseJsonParserMethodMatch, + liveUrlResponseJsonParserMethodMatch, ) - fingerprintsToPatch.forEachIndexed { index, fingerprint -> - - fingerprint.method.apply { + methodsToPatch.forEach { match -> + match.method.apply { val putSharingUrlIndex = indexOfFirstInstruction( - index, + match[0], Opcode.IPUT_OBJECT ) val sharingUrlRegister = getInstruction(putSharingUrlIndex).registerA diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/Fingerprints.kt new file mode 100644 index 0000000000..b7703b3147 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/Fingerprints.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.instagram.misc.share + +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.name +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.Opcode + +internal val BytecodePatchContext.permalinkResponseJsonParserMethodMatch by composingFirstMethod { + name("parseFromJson") + instructions("permalink"()) + opcodes( + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL + ) +} + +internal val BytecodePatchContext.storyUrlResponseJsonParserMethodMatch by composingFirstMethod { + name("parseFromJson") + instructions("story_item_to_share_url"()) +} + +internal val BytecodePatchContext.profileUrlResponseJsonParserMethodMatch by composingFirstMethod { + name("parseFromJson") + instructions("profile_to_share_url"()) +} + +internal val BytecodePatchContext.liveUrlResponseJsonParserMethodMatch by composingFirstMethod { + name("parseFromJson") + instructions("live_to_share_url"()) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/UrlResponseJsonParserFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/UrlResponseJsonParserFingerprint.kt deleted file mode 100644 index fd6fbf5c48..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/UrlResponseJsonParserFingerprint.kt +++ /dev/null @@ -1,32 +0,0 @@ -package app.revanced.patches.instagram.misc.share - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.Opcode - -internal val TARGET_STRING_ARRAY = arrayOf( - "permalink", - "story_item_to_share_url", - "profile_to_share_url", - "live_to_share_url", -) - -internal val permalinkResponseJsonParserFingerprint = fingerprint { - strings(TARGET_STRING_ARRAY[0]) - opcodes(Opcode.NEW_INSTANCE,Opcode.INVOKE_DIRECT,Opcode.INVOKE_VIRTUAL) - custom { method, _ -> method.name == "parseFromJson" } -} - -internal val storyUrlResponseJsonParserFingerprint = fingerprint { - strings(TARGET_STRING_ARRAY[1]) - custom { method, _ -> method.name == "parseFromJson" } -} - -internal val profileUrlResponseJsonParserFingerprint = fingerprint { - strings(TARGET_STRING_ARRAY[2]) - custom { method, _ -> method.name == "parseFromJson" } -} - -internal val liveUrlResponseJsonParserFingerprint = fingerprint { - strings(TARGET_STRING_ARRAY[3]) - custom { method, _ -> method.name == "parseFromJson" } -} 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..d449bc818b 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 @@ -1,33 +1,33 @@ package app.revanced.patches.instagram.misc.share.domain -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.stringOption import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch import app.revanced.patches.instagram.misc.share.editShareLinksPatch -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, - description = PATCH_DESCRIPTION_CHANGE_LINK_SHARING_DOMAIN, - use = false + name = "Change link sharing domain", + description = "Replaces the domain name of shared links.", + use = false, ) { compatibleWith("com.instagram.android") dependsOn(sharedExtensionPatch) val customDomainHost by stringOption( - key = "domainName", default = "imginn.com", - title = "Domain name", - description = "The domain name to use when sharing links." + name = "Domain name", + description = "The domain name to use when sharing links.", ) - execute { - getCustomShareDomainFingerprint.method.returnEarly(customDomainHost!!) + apply { + getCustomShareDomainMethod.returnEarly(customDomainHost!!) editShareLinksPatch { index, register -> addInstructions( @@ -35,7 +35,7 @@ val changeLinkSharingDomainPatch = bytecodePatch( """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setCustomShareDomain(Ljava/lang/String;)Ljava/lang/String; move-result-object v$register - """ + """, ) } } 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..44ab07b6d8 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 @@ -1,16 +1,13 @@ package app.revanced.patches.instagram.misc.share.domain -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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 { +internal val BytecodePatchContext.getCustomShareDomainMethod by gettingFirstMethodDeclaratively { + name("getCustomShareDomain") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters() - custom { method, classDef -> - method.name == "getCustomShareDomain" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Ljava/lang/String;") + parameterTypes() } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/privacy/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/privacy/SanitizeSharingLinksPatch.kt index a73099a225..838fcba8ab 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/privacy/SanitizeSharingLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/share/privacy/SanitizeSharingLinksPatch.kt @@ -1,32 +1,30 @@ package app.revanced.patches.instagram.misc.share.privacy -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch import app.revanced.patches.instagram.misc.share.editShareLinksPatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS -import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/instagram/misc/share/privacy/SanitizeSharingLinksPatch;" @Suppress("unused") val sanitizeSharingLinksPatch = bytecodePatch( - name = PATCH_NAME_SANITIZE_SHARING_LINKS, - description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS, + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from shared links.", ) { compatibleWith("com.instagram.android") dependsOn(sharedExtensionPatch) - execute { + apply { editShareLinksPatch { index, register -> addInstructions( index, """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeSharingLink(Ljava/lang/String;)Ljava/lang/String; move-result-object v$register - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/Fingerprints.kt index 47ebe189b3..6f4d9a89b7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/Fingerprints.kt @@ -1,20 +1,17 @@ package app.revanced.patches.instagram.misc.signature -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val isValidSignatureClassFingerprint = fingerprint { - strings("The provider for uri '", "' is not trusted: ") -} +context(_: BytecodePatchContext) +internal fun ClassDef.getIsValidSignatureClassMethod() = firstMethodDeclaratively( + "The provider for uri '", + "' is not trusted: ", +) -internal val isValidSignatureMethodFingerprint = fingerprint { - parameters("L", "Z") - returns("Z") - custom { method, _ -> - method.indexOfFirstInstruction { - getReference()?.name == "keySet" - } >= 0 - } +internal val BytecodePatchContext.isValidSignatureMethodMethod by gettingFirstMethodDeclaratively { + parameterTypes("L", "Z") + returnType("Z") + instructions(method("keySet")) } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/SignatureCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/SignatureCheckPatch.kt index 0493be62de..3ee3f82380 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/SignatureCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/misc/signature/SignatureCheckPatch.kt @@ -1,21 +1,19 @@ package app.revanced.patches.instagram.misc.signature +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val signatureCheckPatch = bytecodePatch( +val disableSignatureCheckPatch = bytecodePatch( name = "Disable signature check", description = "Disables the signature check that can cause the app to crash on startup. " + - "Including this patch may cause issues with sharing or opening external Instagram links.", - use = false + "Using this patch may cause issues with sharing or opening external Instagram links.", + use = false, ) { compatibleWith("com.instagram.android") - execute { - isValidSignatureMethodFingerprint - .match(isValidSignatureClassFingerprint.classDef) - .method - .returnEarly(true) + apply { + isValidSignatureMethodMethod.immutableClassDef.getIsValidSignatureClassMethod().returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/reels/DisableReelsScrollingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/reels/DisableReelsScrollingPatch.kt index 059a300655..547a826581 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/reels/DisableReelsScrollingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/reels/DisableReelsScrollingPatch.kt @@ -1,34 +1,35 @@ -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +package app.revanced.patches.instagram.reels + +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.instagram.reels.clipsSwipeRefreshLayoutOnInterceptTouchEventFingerprint -import app.revanced.patches.instagram.reels.clipsViewPagerImplGetViewAtIndexFingerprint import app.revanced.util.returnEarly @Suppress("unused") val disableReelsScrollingPatch = bytecodePatch( name = "Disable Reels scrolling", description = "Disables the endless scrolling behavior in Instagram Reels, preventing swiping to the next Reel. " + - "Note: On a clean install, the 'Tip' animation may appear but will stop on its own after a few seconds.", + "Note: On a clean install, the 'Tip' animation may appear but will stop on its own after a few seconds.", use = false ) { compatibleWith("com.instagram.android") - execute { - val viewPagerField = clipsViewPagerImplGetViewAtIndexFingerprint.classDef.fields.first { + apply { + val viewPagerField = clipsViewPagerImplGetViewAtIndexMethod.classDef.fields.first { it.type == "Landroidx/viewpager2/widget/ViewPager2;" } // Disable user input on the ViewPager2 to prevent scrolling. - clipsViewPagerImplGetViewAtIndexFingerprint.method.addInstructions( + clipsViewPagerImplGetViewAtIndexMethod.addInstructions( 0, """ iget-object v0, p0, $viewPagerField const/4 v1, 0x0 invoke-virtual { v0, v1 }, Landroidx/viewpager2/widget/ViewPager2;->setUserInputEnabled(Z)V - """ + """, ) // Return false in onInterceptTouchEvent to disable pull-to-refresh. - clipsSwipeRefreshLayoutOnInterceptTouchEventFingerprint.method.returnEarly(false) + clipsSwipeRefreshLayoutOnInterceptTouchEventMethod.returnEarly(false) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/reels/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/reels/Fingerprints.kt index d0b6b62667..335c90c8ee 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/reels/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/reels/Fingerprints.kt @@ -1,13 +1,14 @@ package app.revanced.patches.instagram.reels -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext -internal val clipsViewPagerImplGetViewAtIndexFingerprint = fingerprint { - strings("ClipsViewPagerImpl_getViewAtIndex") -} - -internal val clipsSwipeRefreshLayoutOnInterceptTouchEventFingerprint = fingerprint { - parameters("Landroid/view/MotionEvent;") - custom { _, classDef -> classDef.type == "Linstagram/features/clips/viewer/ui/ClipsSwipeRefreshLayout;" } +internal val BytecodePatchContext.clipsViewPagerImplGetViewAtIndexMethod by gettingFirstMethodDeclaratively("ClipsViewPagerImpl_getViewAtIndex") + +internal val BytecodePatchContext.clipsSwipeRefreshLayoutOnInterceptTouchEventMethod by gettingFirstMethodDeclaratively { + parameterTypes("Landroid/view/MotionEvent;") + definingClass("Linstagram/features/clips/viewer/ui/ClipsSwipeRefreshLayout;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/shared/Utils.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/shared/Utils.kt deleted file mode 100644 index 522257aa89..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/shared/Utils.kt +++ /dev/null @@ -1,25 +0,0 @@ -package app.revanced.patches.instagram.shared - -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.BytecodePatchContext -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -context(BytecodePatchContext) -internal fun Fingerprint.replaceStringWithBogus( - targetString: String, -) { - val targetStringIndex = stringMatches!!.first { match -> match.string == targetString }.index - val targetStringRegister = method.getInstruction(targetStringIndex).registerA - - /** - * Replaces the 'target string' with 'BOGUS'. - * This is usually done when we need to override a JSON key or url, - * to skip with a random string that is not a valid JSON key. - */ - method.replaceInstruction( - targetStringIndex, - "const-string v$targetStringRegister, \"BOGUS\"", - ) -} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/DisableStoryAutoFlippingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/DisableStoryAutoFlippingPatch.kt index 172f7dce75..abcd236417 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/DisableStoryAutoFlippingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/DisableStoryAutoFlippingPatch.kt @@ -7,11 +7,11 @@ import app.revanced.util.returnEarly val disableStoryAutoFlippingPatch = bytecodePatch( name = "Disable story auto flipping", description = "Disable stories automatically flipping/skipping after some seconds.", - use = false + use = false, ) { compatibleWith("com.instagram.android") - execute { - onStoryTimeoutActionFingerprint.method.returnEarly() + apply { + onStoryTimeoutActionMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/Fingerprints.kt index 3562dbde6b..647fd0fee9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/story/flipping/Fingerprints.kt @@ -1,12 +1,13 @@ package app.revanced.patches.instagram.story.flipping -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val onStoryTimeoutActionFingerprint = fingerprint { - parameters("Ljava/lang/Object;") - returns("V") - strings("userSession") - custom { _, classDef -> - classDef.type == "Linstagram/features/stories/fragment/ReelViewerFragment;" - } +internal val BytecodePatchContext.onStoryTimeoutActionMethod by gettingFirstMethodDeclaratively("userSession") { + parameterTypes("Ljava/lang/Object;") + returnType("V") + definingClass("Linstagram/features/stories/fragment/ReelViewerFragment;") } 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..3e68519d4e 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 @@ -1,11 +1,12 @@ package app.revanced.patches.irplus.ad +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val irplusAdsFingerprint = fingerprint { +internal val BytecodePatchContext.irplusAdsMethod by gettingFirstMethodDeclaratively("TAGGED") { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - parameters("L", "Z") - strings("TAGGED") + parameterTypes("L", "Z") } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt index 44b76f5fc4..a8bbf6e727 100644 --- a/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt @@ -1,17 +1,15 @@ package app.revanced.patches.irplus.ad -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") -val removeAdsPatch = bytecodePatch( - name = "Remove ads", -) { +val removeAdsPatch = bytecodePatch("Remove ads") { compatibleWith("net.binarymode.android.irplus") - execute { + apply { // By overwriting the second parameter of the method, // the view which holds the advertisement is removed. - irplusAdsFingerprint.method.addInstruction(0, "const/4 p2, 0x0") + irplusAdsMethod.addInstruction(0, "const/4 p2, 0x0") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/Fingerprints.kt index 97f8465ae1..9b992b49d0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.kleinanzeigen.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val getLibertyInitFingerprint = fingerprint { - custom { method, classDef -> - method.name == "init" && classDef.endsWith("/Liberty;") - } +internal val BytecodePatchContext.getLibertyInitMethod by gettingFirstMethodDeclaratively { + name("init") + definingClass("/Liberty;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/HideAdsPatch.kt index 87f689014c..403301d916 100644 --- a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/ads/HideAdsPatch.kt @@ -10,7 +10,7 @@ val hideAdsPatch = bytecodePatch( ) { compatibleWith("com.ebay.kleinanzeigen") - execute { - getLibertyInitFingerprint.method.returnEarly() + apply { + getLibertyInitMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/Fingerprints.kt index 79a050f9e2..f275dd93ca 100644 --- a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/Fingerprints.kt @@ -1,9 +1,8 @@ package app.revanced.patches.kleinanzeigen.hide_pur -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.patch.BytecodePatchContext -internal val getShowAdFreeSubscriptionFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getShowAdFreeSubscription" - } +internal val BytecodePatchContext.getShowAdFreeSubscriptionFingerprint by gettingFirstMethod { + name == "getShowAdFreeSubscription" } diff --git a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/HidePurPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/HidePurPatch.kt index a55f675ec0..0956885b51 100644 --- a/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/HidePurPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/kleinanzeigen/hide_pur/HidePurPatch.kt @@ -10,7 +10,7 @@ val hidePurPatch = bytecodePatch( ) { compatibleWith("com.ebay.kleinanzeigen") - execute { - getShowAdFreeSubscriptionFingerprint.method.returnEarly(false) + apply { + getShowAdFreeSubscriptionFingerprint.returnEarly(false) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/Fingerprints.kt index 423c262846..2b2234237f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/Fingerprints.kt @@ -1,29 +1,28 @@ package app.revanced.patches.letterboxd.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext internal const val admobHelperClassName = "Lcom/letterboxd/letterboxd/helpers/AdmobHelper;" -internal val admobHelperSetShowAdsFingerprint = fingerprint { - custom { method, classDef -> - method.name == "setShowAds" && classDef.type == admobHelperClassName - } +internal val BytecodePatchContext.admobHelperSetShowAdsMethod by gettingFirstMethodDeclaratively { + name("setShowAds") + definingClass(admobHelperClassName) } -internal val admobHelperShouldShowAdsFingerprint = fingerprint { - custom { method, classDef -> - method.name == "shouldShowAds" && classDef.type == admobHelperClassName - } +internal val BytecodePatchContext.admobHelperShouldShowAdsMethod by gettingFirstMethodDeclaratively { + name("shouldShowAds") + definingClass(admobHelperClassName) } -internal val filmFragmentShowAdsFingerprint = fingerprint { - custom { method, classDef -> - method.name == "showAds" && classDef.type.endsWith("/FilmFragment;") - } +internal val BytecodePatchContext.filmFragmentShowAdsMethod by gettingFirstMethodDeclaratively { + name("showAds") + definingClass("/FilmFragment;") } -internal val memberExtensionShowAdsFingerprint = fingerprint { - custom { method, classDef -> - method.name == "showAds" && classDef.type.endsWith("/AMemberExtensionKt;") - } -} \ No newline at end of file +internal val BytecodePatchContext.memberExtensionShowAdsMethod by gettingFirstMethodDeclaratively { + name("showAds") + definingClass("/AMemberExtensionKt;") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/HideAdsPatch.kt index 8a2ff87f07..1296844822 100644 --- a/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/letterboxd/ads/HideAdsPatch.kt @@ -1,20 +1,17 @@ - package app.revanced.patches.letterboxd.ads -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.letterboxd.letterboxd") - execute { - admobHelperSetShowAdsFingerprint.method.addInstruction(0, "const p1, 0x0") - listOf(admobHelperShouldShowAdsFingerprint, filmFragmentShowAdsFingerprint, memberExtensionShowAdsFingerprint).forEach { - it.method.returnEarly() + apply { + admobHelperSetShowAdsMethod.addInstruction(0, "const p1, 0x0") + listOf(admobHelperShouldShowAdsMethod, filmFragmentShowAdsMethod, memberExtensionShowAdsMethod).forEach { + it.returnEarly() } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/Fingerprints.kt index 1b549cd573..ca46ac6bca 100644 --- a/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.letterboxd.unlock.unlockAppIcons -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val getCanChangeAppIconFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getCanChangeAppIcon" && classDef.type.endsWith("SettingsAppIconFragment;") - } +internal val BytecodePatchContext.getCanChangeAppIconMethod by gettingFirstMethodDeclaratively { + name("getCanChangeAppIcon") + definingClass("SettingsAppIconFragment;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatch.kt index 54d6f3df9a..4c20a5ed1a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatch.kt @@ -1,16 +1,13 @@ - package app.revanced.patches.letterboxd.unlock.unlockAppIcons import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val unlockAppIconsPatch = bytecodePatch( - name = "Unlock app icons", -) { +val unlockAppIconsPatch = bytecodePatch("Unlock app icons") { compatibleWith("com.letterboxd.letterboxd") - execute { - getCanChangeAppIconFingerprint.method.returnEarly(true) + apply { + getCanChangeAppIconMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/bypassVersionCheck/DisableVersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/bypassVersionCheck/DisableVersionCheckPatch.kt deleted file mode 100644 index 9d23b7716c..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/bypassVersionCheck/DisableVersionCheckPatch.kt +++ /dev/null @@ -1,23 +0,0 @@ -package app.revanced.patches.lightroom.misc.version - -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.bytecodePatch -import com.android.tools.smali.dexlib2.Opcode - -@Suppress("unused") -val disableVersionCheckPatch = bytecodePatch( - name = "Disable version check", - description = "Disables the server-side version check that prevents the app from starting.", -) { - compatibleWith("com.adobe.lrmobile"("9.3.0")) - - execute { - refreshRemoteConfigurationFingerprint.method.apply { - val igetIndex = refreshRemoteConfigurationFingerprint.patternMatch!!.endIndex - - // This value represents the server command to clear all version restrictions. - val statusForceReset = "-0x2"; - replaceInstruction(igetIndex, "const/4 v1, $statusForceReset") - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/bypassVersionCheck/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/bypassVersionCheck/Fingerprints.kt deleted file mode 100644 index 67900c31ea..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/bypassVersionCheck/Fingerprints.kt +++ /dev/null @@ -1,18 +0,0 @@ -package app.revanced.patches.lightroom.misc.version - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -internal val refreshRemoteConfigurationFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - strings( - "com.adobe.lrmobile.denylisted_version_set_key", - "com.adobe.lrmobile.app_min_version_key" - ) - opcodes( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IGET, // Overwrite this instruction to disable the check. - ) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt index e29608de0e..0f0f495d9d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt @@ -1,19 +1,16 @@ package app.revanced.patches.lightroom.misc.login -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") -val disableMandatoryLoginPatch = bytecodePatch( - name = "Disable mandatory login", -) { +val disableMandatoryLoginPatch = bytecodePatch("Disable mandatory login") { compatibleWith("com.adobe.lrmobile"("9.3.0")) - execute { - isLoggedInFingerprint.method.apply { - val index = implementation!!.instructions.lastIndex - 1 - // Set isLoggedIn = true. - replaceInstruction(index, "const/4 v0, 0x1") - } + apply { + val index = isLoggedInMethod.instructions.lastIndex - 1 + // Set isLoggedIn = true. + isLoggedInMethod.replaceInstruction(index, "const/4 v0, 0x1") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt index 6345541e14..cae148db09 100644 --- a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt @@ -1,12 +1,16 @@ package app.revanced.patches.lightroom.misc.login -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val isLoggedInFingerprint = fingerprint { +internal val BytecodePatchContext.isLoggedInMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) - returns("Z") + returnType("Z") opcodes( Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT_OBJECT, diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt index 5a00dc68c1..4f580f2547 100644 --- a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt @@ -1,17 +1,20 @@ package app.revanced.patches.lightroom.misc.premium -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val hasPurchasedFingerprint = fingerprint { +internal val BytecodePatchContext.hasPurchasedMethod by gettingFirstMethodDeclaratively("isPurchaseDoneRecently = true, access platform profile present? = ") { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("Z") + returnType("Z") opcodes( Opcode.SGET_OBJECT, Opcode.CONST_4, Opcode.CONST_4, Opcode.CONST_4, ) - strings("isPurchaseDoneRecently = true, access platform profile present? = ") } diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt index e54f3169a1..3ef0a91efd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt @@ -1,16 +1,14 @@ package app.revanced.patches.lightroom.misc.premium -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") -val unlockPremiumPatch = bytecodePatch( - name = "Unlock Premium", -) { +val unlockPremiumPatch = bytecodePatch("Unlock Premium") { compatibleWith("com.adobe.lrmobile"("9.3.0")) - execute { + apply { // Set hasPremium = true. - hasPurchasedFingerprint.method.replaceInstruction(2, "const/4 v2, 0x1") + hasPurchasedMethod.replaceInstruction(2, "const/4 v2, 0x1") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/version/DisableVersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/version/DisableVersionCheckPatch.kt new file mode 100644 index 0000000000..1147655409 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/version/DisableVersionCheckPatch.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.lightroom.misc.version + +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableVersionCheckPatch = bytecodePatch( + name = "Disable version check", + description = "Disables the server-side version check that prevents the app from starting.", +) { + compatibleWith("com.adobe.lrmobile"("9.3.0")) + + apply { + val igetIndex = refreshRemoteConfigurationMethodMatch[-1] + + // This value represents the server command to clear all version restrictions. + val statusForceReset = "-0x2" + refreshRemoteConfigurationMethodMatch.method.replaceInstruction(igetIndex, "const/4 v1, $statusForceReset") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/version/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/version/Fingerprints.kt new file mode 100644 index 0000000000..f8be83517a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/version/Fingerprints.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.lightroom.misc.version + +import app.revanced.patcher.accessFlags +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val BytecodePatchContext.refreshRemoteConfigurationMethodMatch by composingFirstMethod( + "com.adobe.lrmobile.denylisted_version_set_key", + "com.adobe.lrmobile.app_min_version_key", +) { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET, // Overwrite this instruction to disable the check. + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt index 0dfbf5cdab..f40e9d0dce 100644 --- a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt @@ -1,13 +1,14 @@ package app.revanced.patches.memegenerator.detection.license -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val licenseValidationFingerprint = fingerprint { +internal val BytecodePatchContext.licenseValidationMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("Landroid/content/Context;") + returnType("Z") + parameterTypes("Landroid/content/Context;") opcodes( Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT_WIDE, diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt index 8b4a417348..ced1a10790 100644 --- a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt @@ -1,19 +1,13 @@ package app.revanced.patches.memegenerator.detection.license -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly val licenseValidationPatch = bytecodePatch( description = "Disables Firebase license validation.", ) { - execute { - licenseValidationFingerprint.method.replaceInstructions( - 0, - """ - const/4 p0, 0x1 - return p0 - """, - ) + apply { + licenseValidationMethod.returnEarly(true) } } 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..6bb50317f3 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 @@ -1,13 +1,14 @@ package app.revanced.patches.memegenerator.detection.signature -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val verifySignatureFingerprint = fingerprint(fuzzyPatternScanThreshold = 2) { +internal val BytecodePatchContext.verifySignatureMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("Landroid/app/Activity;") + returnType("Z") + parameterTypes("Landroid/app/Activity;") opcodes( Opcode.SGET_OBJECT, Opcode.IF_NEZ, diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt index c6b05ae8db..e4679f4da9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt @@ -1,19 +1,13 @@ package app.revanced.patches.memegenerator.detection.signature -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly val signatureVerificationPatch = bytecodePatch( description = "Disables detection of incorrect signature.", ) { - execute { - verifySignatureFingerprint.method.replaceInstructions( - 0, - """ - const/4 p0, 0x1 - return p0 - """, - ) + apply { + verifySignatureMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt index 1f16bb10ec..68a9b70b42 100644 --- a/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt @@ -1,13 +1,14 @@ package app.revanced.patches.memegenerator.misc.pro -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val isFreeVersionFingerprint = fingerprint { +internal val BytecodePatchContext.isFreeVersionMethod by gettingFirstMethodDeclaratively("free") { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Ljava/lang/Boolean;") - parameters("Landroid/content/Context;") + returnType("Ljava/lang/Boolean;") + parameterTypes("Landroid/content/Context;") opcodes( Opcode.SGET, Opcode.INVOKE_VIRTUAL, @@ -17,5 +18,4 @@ internal val isFreeVersionFingerprint = fingerprint { Opcode.MOVE_RESULT, Opcode.IF_EQZ ) - strings("free") } diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt index b74e2d3a8d..026a25d5ad 100644 --- a/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt @@ -1,20 +1,18 @@ package app.revanced.patches.memegenerator.misc.pro -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.extensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.memegenerator.detection.license.licenseValidationPatch import app.revanced.patches.memegenerator.detection.signature.signatureVerificationPatch @Suppress("unused") -val unlockProVersionPatch = bytecodePatch( - name = "Unlock pro", -) { +val unlockProPatch = bytecodePatch("Unlock pro") { dependsOn(signatureVerificationPatch, licenseValidationPatch) compatibleWith("com.zombodroid.MemeGenerator"("4.6364", "4.6370", "4.6375", "4.6377")) - execute { - isFreeVersionFingerprint.method.replaceInstructions( + apply { + isFreeVersionMethod.replaceInstructions( 0, """ sget-object p0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean; diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt index 185c88f94c..b6ce2a032c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt @@ -1,35 +1,35 @@ package app.revanced.patches.messenger.inbox -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue -internal val createInboxSubTabsFingerprint = fingerprint { - returns("V") +internal val BytecodePatchContext.createInboxSubTabsMethod by gettingFirstMethodDeclaratively { + name("run") + returnType("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) opcodes( Opcode.CONST_4, Opcode.INVOKE_VIRTUAL, Opcode.RETURN_VOID, ) - custom { method, classDef -> - method.name == "run" && - classDef.fields.any any@{ field -> - if (field.name != "__redex_internal_original_name") return@any false - (field.initialValue as? StringEncodedValue)?.value == "InboxSubtabsItemSupplierImplementation\$onSubscribe\$1" - } + custom { + immutableClassDef.fields.any { field -> + if (field.name != "__redex_internal_original_name") return@any false + (field.initialValue as? StringEncodedValue)?.value == "InboxSubtabsItemSupplierImplementation\$onSubscribe\$1" + } } } -internal val loadInboxAdsFingerprint = fingerprint { - returns("V") - strings( - "ads_load_begin", - "inbox_ads_fetch_start", +internal val BytecodePatchContext.loadInboxAdsMethod by gettingFirstMethodDeclaratively( + "ads_load_begin", + "inbox_ads_fetch_start" +) { + returnType("V") + definingClass( + "Lcom/facebook/messaging/business/inboxads/plugins/inboxads/itemsupplier/" + + "InboxAdsItemSupplierImplementation;" ) - custom { method, _ -> - method.definingClass == "Lcom/facebook/messaging/business/inboxads/plugins/inboxads/itemsupplier/" + - "InboxAdsItemSupplierImplementation;" - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt index 090ffd9f82..dd9994ae70 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.messenger.inbox -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,7 +10,7 @@ val hideInboxAdsPatch = bytecodePatch( ) { compatibleWith("com.facebook.orca") - execute { - loadInboxAdsFingerprint.method.replaceInstruction(0, "return-void") + apply { + loadInboxAdsMethod.replaceInstruction(0, "return-void") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt index 2d190615f3..cca30b51b1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.messenger.inbox -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,7 +10,7 @@ val hideInboxSubtabsPatch = bytecodePatch( ) { compatibleWith("com.facebook.orca") - execute { - createInboxSubTabsFingerprint.method.replaceInstruction(2, "const/4 v0, 0x0") + apply { + createInboxSubTabsMethod.replaceInstruction(2, "const/4 v0, 0x0") } } 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/layout/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/layout/Fingerprints.kt index 2abc12367c..d0cce5ef00 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/layout/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/layout/Fingerprints.kt @@ -1,9 +1,14 @@ package app.revanced.patches.messenger.layout -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke -internal val isFacebookButtonEnabledFingerprint = fingerprint { - parameters() - returns("Z") - strings("FacebookButtonTabButtonImplementation") +internal val BytecodePatchContext.isFacebookButtonEnabledMethod by gettingFirstMethodDeclaratively { + parameterTypes() + returnType("Z") + instructions("FacebookButtonTabButtonImplementation"(String::contains)) } diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/layout/HideFacebookButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/layout/HideFacebookButtonPatch.kt index 9e003d8710..cb2a47316d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/messenger/layout/HideFacebookButtonPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/layout/HideFacebookButtonPatch.kt @@ -6,11 +6,11 @@ import app.revanced.util.returnEarly @Suppress("unused") val hideFacebookButtonPatch = bytecodePatch( name = "Hide Facebook button", - description = "Hides the Facebook button in the top toolbar." + description = "Hides the Facebook button in the top toolbar.", ) { compatibleWith("com.facebook.orca") - execute { - isFacebookButtonEnabledFingerprint.method.returnEarly(false) + apply { + isFacebookButtonEnabledMethod.returnEarly(false) } } 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..2e13946d92 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 @@ -1,25 +1,23 @@ package app.revanced.patches.messenger.metaai +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -import app.revanced.patcher.fingerprint -internal val getMobileConfigBoolFingerprint = fingerprint { - parameters("J") - returns("Z") +internal val BytecodePatchContext.getMobileConfigBoolMethodMatch by composingFirstMethod { + parameterTypes("J") + returnType("Z") opcodes(Opcode.RETURN) - custom { _, classDef -> - classDef.interfaces.contains("Lcom/facebook/mobileconfig/factory/MobileConfigUnsafeContext;") - } + custom { "Lcom/facebook/mobileconfig/factory/MobileConfigUnsafeContext;" in immutableClassDef.interfaces } } -internal val metaAIKillSwitchCheckFingerprint = fingerprint { - strings("SearchAiagentImplementationsKillSwitch") +internal val BytecodePatchContext.metaAIKillSwitchCheckMethodMatch by composingFirstMethod { opcodes(Opcode.CONST_WIDE) + instructions("SearchAiagentImplementationsKillSwitch"(String::contains)) } -internal val extensionMethodFingerprint = fingerprint { - strings("REPLACED_BY_PATCH") - custom { method, classDef -> - method.name == EXTENSION_METHOD_NAME && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } +internal val BytecodePatchContext.extensionMethodMatch by composingFirstMethod { + name(EXTENSION_METHOD_NAME) + definingClass(EXTENSION_CLASS_DESCRIPTOR) + instructions("REPLACED_BY_PATCH"()) } 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..7b0d74908a 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 @@ -1,8 +1,8 @@ package app.revanced.patches.messenger.metaai -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.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.messenger.misc.extension.sharedExtensionPatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -14,38 +14,36 @@ internal const val EXTENSION_METHOD_NAME = "overrideBooleanFlag" @Suppress("unused") val removeMetaAIPatch = bytecodePatch( name = "Remove Meta AI", - description = "Removes UI elements related to Meta AI." + description = "Removes UI elements related to Meta AI.", ) { compatibleWith("com.facebook.orca") dependsOn(sharedExtensionPatch) - execute { - getMobileConfigBoolFingerprint.method.apply { - val returnIndex = getMobileConfigBoolFingerprint.patternMatch!!.startIndex - val returnRegister = getInstruction(returnIndex).registerA + apply { + getMobileConfigBoolMethodMatch.let { + val returnIndex = it[0] + val returnRegister = it.method.getInstruction(returnIndex).registerA - addInstructions( + it.method.addInstructions( returnIndex, """ invoke-static { p1, p2, v$returnRegister }, $EXTENSION_CLASS_DESCRIPTOR->$EXTENSION_METHOD_NAME(JZ)Z move-result v$returnRegister - """ + """, ) } // 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 + val relevantDigits = metaAIKillSwitchCheckMethodMatch.let { + it.method.getInstruction(it[0]).wideLiteral }.toString().substring(0, 7) // Replace placeholder in the extension method. - with(extensionMethodFingerprint) { - method.replaceInstruction( - stringMatches!!.first().index, - """ - const-string v1, "$relevantDigits" - """ + extensionMethodMatch.let { + it.method.replaceInstruction( + it[0], + "const-string v1, \"$relevantDigits\"", ) } } 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..d0cb18e45f 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,16 @@ -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.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import com.android.tools.smali.dexlib2.AccessFlags + +internal val BytecodePatchContext.adInjectorMethod by gettingFirstMethodDeclaratively( + "SponsoredContentController.insertItem", +) { + accessFlags(AccessFlags.PRIVATE) + returnType("Z") + parameterTypes("L", "L") +} 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/microsoft/officelens/misc/onedrive/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/microsoft/officelens/misc/onedrive/Fingerprints.kt index 570dda0875..f965bf5b0b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/microsoft/officelens/misc/onedrive/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/microsoft/officelens/misc/onedrive/Fingerprints.kt @@ -1,11 +1,11 @@ package app.revanced.patches.microsoft.officelens.misc.onedrive -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val hasMigratedToOneDriveFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("FREManager;") && method.name == "getMigrationStage" - } +internal val BytecodePatchContext.hasMigratedToOneDriveMethod by gettingFirstMethodDeclaratively { + name("getMigrationStage") + definingClass("FREManager;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/microsoft/officelens/misc/onedrive/HideOneDriveMigrationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/microsoft/officelens/misc/onedrive/HideOneDriveMigrationPatch.kt index a772a7b12f..b2ce063407 100644 --- a/patches/src/main/kotlin/app/revanced/patches/microsoft/officelens/misc/onedrive/HideOneDriveMigrationPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/microsoft/officelens/misc/onedrive/HideOneDriveMigrationPatch.kt @@ -10,8 +10,8 @@ val hideOneDriveMigrationPatch = bytecodePatch( ) { compatibleWith("com.microsoft.office.officelens") - execute { - hasMigratedToOneDriveFingerprint.method.replaceInstructions( + apply { + hasMigratedToOneDriveMethod.replaceInstructions( 0, """ sget-object v0, Lcom/microsoft/office/officelens/scansMigration/LensMigrationStage;->PreMigration:Lcom/microsoft/office/officelens/scansMigration/LensMigrationStage; @@ -19,4 +19,4 @@ val hideOneDriveMigrationPatch = bytecodePatch( """, ) } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt index 14105f762a..6187e22156 100644 --- a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt @@ -1,12 +1,11 @@ package app.revanced.patches.mifitness.misc.locale -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val syncBluetoothLanguageFingerprint = fingerprint { +internal val BytecodePatchContext.syncBluetoothLanguageMethodMatch by composingFirstMethod { + name("syncBluetoothLanguage") + definingClass("Lcom/xiaomi/fitness/devicesettings/DeviceSettingsSyncer") opcodes(Opcode.MOVE_RESULT_OBJECT) - custom { method, _ -> - method.name == "syncBluetoothLanguage" && - method.definingClass == "Lcom/xiaomi/fitness/devicesettings/DeviceSettingsSyncer;" - } } 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..1e65344c5e 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 @@ -1,7 +1,7 @@ package app.revanced.patches.mifitness.misc.locale -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.mifitness.misc.login.fixLoginPatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -15,12 +15,12 @@ val forceEnglishLocalePatch = bytecodePatch( dependsOn(fixLoginPatch) - execute { - syncBluetoothLanguageFingerprint.method.apply { - val resolvePhoneLocaleInstruction = syncBluetoothLanguageFingerprint.patternMatch!!.startIndex - val registerIndexToUpdate = getInstruction(resolvePhoneLocaleInstruction).registerA + apply { + syncBluetoothLanguageMethodMatch.let { + val resolvePhoneLocaleInstruction = it[0] + val registerIndexToUpdate = it.method.getInstruction(resolvePhoneLocaleInstruction).registerA - replaceInstruction( + it.method.replaceInstruction( resolvePhoneLocaleInstruction, "const-string v$registerIndexToUpdate, \"en_gb\"", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt index e3eee24991..2fad6bb988 100644 --- a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt @@ -1,12 +1,14 @@ package app.revanced.patches.mifitness.misc.login -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val xiaomiAccountManagerConstructorFingerprint = fingerprint { +internal val BytecodePatchContext.xiaomiAccountManagerConstructorMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) - parameters("Landroid/content/Context;", "Z") - custom { method, _ -> - method.definingClass == "Lcom/xiaomi/passport/accountmanager/XiaomiAccountManager;" - } + parameterTypes("Landroid/content/Context;", "Z") + definingClass("Lcom/xiaomi/passport/accountmanager/XiaomiAccountManager;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt index 093a5d4f14..5208f694be 100644 --- a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt @@ -1,15 +1,16 @@ package app.revanced.patches.mifitness.misc.login -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch +@Suppress("ObjectPropertyName") val fixLoginPatch = bytecodePatch( name = "Fix login", description = "Fixes login for uncertified Mi Fitness app", ) { compatibleWith("com.xiaomi.wearable") - execute { - xiaomiAccountManagerConstructorFingerprint.method.addInstruction(0, "const/16 p2, 0x0") + apply { + xiaomiAccountManagerConstructorMethod.addInstruction(0, "const/16 p2, 0x0") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt index 6ce0519ada..1d2f125f04 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt @@ -1,13 +1,16 @@ package app.revanced.patches.music.ad.video -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val showVideoAdsParentFingerprint = fingerprint { +internal val BytecodePatchContext.showVideoAdsParentMethodMatch by composingFirstMethod( + "maybeRegenerateCpnAndStatsClient called unexpectedly, but no error.", +) { opcodes( Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_VIRTUAL, Opcode.IGET_OBJECT, ) - strings("maybeRegenerateCpnAndStatsClient called unexpectedly, but no error.") } 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..410a6aff3e 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 @@ -1,6 +1,6 @@ package app.revanced.patches.music.ad.video -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -12,7 +12,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideVideoAdsPatch;" @Suppress("unused") -val hideVideoAdsPatch = bytecodePatch( +val hideMusicVideoAdsPatch = bytecodePatch( name = "Hide music video ads", description = "Adds an option to hide ads that appear while listening to or streaming music videos, podcasts, or songs.", ) { @@ -25,26 +25,26 @@ val hideVideoAdsPatch = bytecodePatch( compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { + apply { addResources("music", "ad.video.hideVideoAdsPatch") PreferenceScreen.ADS.addPreferences( SwitchPreference("revanced_music_hide_video_ads"), ) - navigate(showVideoAdsParentFingerprint.originalMethod) - .to(showVideoAdsParentFingerprint.patternMatch!!.startIndex + 1) + navigate(showVideoAdsParentMethodMatch.immutableMethod) + .to(showVideoAdsParentMethodMatch[0] + 1) .stop() .addInstructions( 0, """ invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->showVideoAds(Z)Z move-result p1 - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt index f0b1974c3f..cc4e23c09a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt @@ -18,11 +18,11 @@ val enableExclusiveAudioPlaybackPatch = bytecodePatch( compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { - allowExclusiveAudioPlaybackFingerprint.method.returnEarly(true) + apply { + allowExclusiveAudioPlaybackMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt index 4ac10dd9bd..dacca3d291 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt @@ -1,13 +1,18 @@ package app.revanced.patches.music.audio.exclusiveaudio -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val allowExclusiveAudioPlaybackFingerprint = fingerprint { +internal val BytecodePatchContext.allowExclusiveAudioPlaybackMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() + returnType("Z") + parameterTypes() opcodes( Opcode.INVOKE_INTERFACE, Opcode.MOVE_RESULT_OBJECT, @@ -18,6 +23,6 @@ internal val allowExclusiveAudioPlaybackFingerprint = fingerprint { Opcode.IF_NEZ, Opcode.IGET_OBJECT, Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT + Opcode.MOVE_RESULT, ) -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt index 13820d29d8..704a2c88a6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt @@ -1,20 +1,20 @@ package app.revanced.patches.music.interaction.permanentrepeat -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val repeatTrackFingerprint = fingerprint { +internal val BytecodePatchContext.repeatTrackMethodMatch by composingFirstMethod("w_st") { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L", "L") + returnType("V") + parameterTypes("L", "L") opcodes( Opcode.IGET_OBJECT, Opcode.IGET_OBJECT, Opcode.SGET_OBJECT, Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, - Opcode.IF_NEZ + Opcode.IF_NEZ, ) - strings("w_st") } 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..aa8a0449d6 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 @@ -1,9 +1,9 @@ package app.revanced.patches.music.interaction.permanentrepeat -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.patch.bytecodePatch -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.music.misc.extension.sharedExtensionPatch @@ -17,7 +17,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/pa @Suppress("unused") val permanentRepeatPatch = bytecodePatch( name = "Permanent repeat", - description = "Adds an option to always repeat even if the playlist ends or another track is played." + description = "Adds an option to always repeat even if the playlist ends or another track is played.", ) { dependsOn( sharedExtensionPatch, @@ -28,21 +28,21 @@ val permanentRepeatPatch = bytecodePatch( compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { + apply { addResources("music", "interaction.permanentrepeat.permanentRepeatPatch") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_music_play_permanent_repeat"), ) - val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex - val repeatIndex = startIndex + 1 + repeatTrackMethodMatch.method.apply { + val startIndex = repeatTrackMethodMatch[-1] + val repeatIndex = startIndex + 1 - repeatTrackFingerprint.method.apply { // Start index is at a branch, but the same // register is clobbered in both branch paths. val freeRegister = findFreeRegister(startIndex + 1) 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..0587878df9 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 @@ -1,18 +1,17 @@ package app.revanced.patches.music.layout.branding -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.gms.Constants.MUSIC_MAIN_ACTIVITY_NAME import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME -import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint +import app.revanced.patches.music.misc.gms.musicActivityOnCreateMethod 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.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow @@ -24,7 +23,7 @@ private val disableSplashAnimationPatch = bytecodePatch { dependsOn(resourceMappingPatch) - execute { + apply { // The existing YT animation usually only shows for a fraction of a second, // and the existing animation does not match the new splash screen // causing the original YT Music logo to momentarily flash on screen as the animation starts. @@ -32,13 +31,13 @@ private val disableSplashAnimationPatch = bytecodePatch { // Could replace the lottie animation file with our own custom animation (app_launch.json), // but the animation is not always the same size as the launch screen and it's still // barely shown. Instead turn off the animation entirely (app will also launch a little faster). - cairoSplashAnimationConfigFingerprint.method.apply { + cairoSplashAnimationConfigMethod.apply { val literalIndex = indexOfFirstLiteralInstructionOrThrow( - resourceMappings["layout", "main_activity_launch_animation"] + ResourceType.LAYOUT["main_activity_launch_animation"], ) val checkCastIndex = indexOfFirstInstructionOrThrow(literalIndex) { opcode == Opcode.CHECK_CAST && - getReference()?.type == "Lcom/airbnb/lottie/LottieAnimationView;" + getReference()?.type == "Lcom/airbnb/lottie/LottieAnimationView;" } val register = getInstruction(checkCastIndex).registerA @@ -48,7 +47,7 @@ private val disableSplashAnimationPatch = bytecodePatch { """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getLottieViewOrNull(Landroid/view/View;)Landroid/view/View; move-result-object v$register - """ + """, ) } } @@ -62,7 +61,7 @@ val customBrandingPatch = baseCustomBrandingPatch( originalAppPackageName = MUSIC_PACKAGE_NAME, isYouTubeMusic = true, numberOfPresetAppNames = 5, - mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint, + getMainActivityOnCreate = { musicActivityOnCreateMethod }, mainActivityName = MUSIC_MAIN_ACTIVITY_NAME, activityAliasNameWithIntents = MUSIC_MAIN_ACTIVITY_NAME, preferenceScreen = PreferenceScreen.GENERAL, @@ -73,8 +72,8 @@ val customBrandingPatch = baseCustomBrandingPatch( compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - } + }, ) 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..d76fb66395 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 @@ -1,12 +1,14 @@ package app.revanced.patches.music.layout.branding -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patches.music.shared.YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE +import app.revanced.patches.shared.misc.mapping.ResourceType -internal val cairoSplashAnimationConfigFingerprint = fingerprint { - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - method.name == "onCreate" && method.definingClass == YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE - } +internal val BytecodePatchContext.cairoSplashAnimationConfigMethod by gettingFirstMethodDeclaratively { + name("onCreate") + returnType("V") + definingClass(YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE) + parameterTypes("Landroid/os/Bundle;") + instructions(ResourceType.LAYOUT("main_activity_launch_animation")) } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/Fingerprints.kt index 31c844aea1..92aa2b6356 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/Fingerprints.kt @@ -1,64 +1,61 @@ package app.revanced.patches.music.layout.buttons -import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction +import app.revanced.patcher.* +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val mediaRouteButtonFingerprint = fingerprint { +internal val BytecodePatchContext.mediaRouteButtonMethod by gettingFirstMethodDeclaratively("MediaRouteButton") { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("Z") - strings("MediaRouteButton") + returnType("Z") } -internal val playerOverlayChipFingerprint = fingerprint { +internal val BytecodePatchContext.playerOverlayChipMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - literal { playerOverlayChip } + returnType("L") + instructions(playerOverlayChip()) } -internal val historyMenuItemFingerprint = fingerprint { +internal val BytecodePatchContext.historyMenuItemMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Landroid/view/Menu;") + returnType("V") + parameterTypes("Landroid/view/Menu;") opcodes( Opcode.INVOKE_INTERFACE, - Opcode.RETURN_VOID + Opcode.RETURN_VOID, ) literal { historyMenuItem } - custom { _, classDef -> - classDef.methods.count() == 5 + custom { + immutableClassDef.methods.count() == 5 } } -internal val historyMenuItemOfflineTabFingerprint = fingerprint { +internal val BytecodePatchContext.historyMenuItemOfflineTabMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Landroid/view/Menu;") + returnType("V") + parameterTypes("Landroid/view/Menu;") opcodes( Opcode.INVOKE_INTERFACE, - Opcode.RETURN_VOID + Opcode.RETURN_VOID, ) - custom { method, _ -> - method.containsLiteralInstruction(historyMenuItem) && - method.containsLiteralInstruction(offlineSettingsMenuItem) - } + + val match = indexedMatcher(items = unorderedAllOf(historyMenuItem(), offlineSettingsMenuItem())) + custom { match(instructions) } } -internal val searchActionViewFingerprint = fingerprint { +internal val BytecodePatchContext.searchActionViewMethod by gettingFirstMethodDeclaratively { + definingClass("/SearchActionProvider;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/view/View;") - parameters() + returnType("Landroid/view/View;") + parameterTypes() literal { searchButton } - custom { _, classDef -> - classDef.type.endsWith("/SearchActionProvider;") - } } -internal val topBarMenuItemImageViewFingerprint = fingerprint { +internal val BytecodePatchContext.topBarMenuItemImageViewMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/view/View;") - parameters() + returnType("Landroid/view/View;") + parameterTypes() literal { topBarMenuItemImageView } } 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..9c2ea8054c 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 @@ -1,17 +1,18 @@ package app.revanced.patches.music.layout.buttons -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.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstMethod +import app.revanced.patcher.immutableClassDef 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.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.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 @@ -33,30 +34,30 @@ internal var topBarMenuItemImageView = -1L private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideButtonsPatch;" @Suppress("unused") -val hideButtons = bytecodePatch( +val hideButtonsPatch = bytecodePatch( name = "Hide buttons", - description = "Adds options to hide the cast, history, notification, and search buttons." + description = "Adds options to hide the cast, history, notification, and search buttons.", ) { dependsOn( sharedExtensionPatch, settingsPatch, addResourcesPatch, - resourceMappingPatch + resourceMappingPatch, ) compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - 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"] + apply { + playerOverlayChip = ResourceType.ID["player_overlay_chip"] + historyMenuItem = ResourceType.ID["history_menu_item"] + offlineSettingsMenuItem = ResourceType.ID["offline_settings_menu_item"] + searchButton = ResourceType.LAYOUT["search_button"] + topBarMenuItemImageView = ResourceType.ID["top_bar_menu_item_image_view"] addResources("music", "layout.buttons.hideButtons") @@ -64,16 +65,16 @@ val hideButtons = bytecodePatch( SwitchPreference("revanced_music_hide_cast_button"), SwitchPreference("revanced_music_hide_history_button"), SwitchPreference("revanced_music_hide_notification_button"), - SwitchPreference("revanced_music_hide_search_button") + SwitchPreference("revanced_music_hide_search_button"), ) // Region for hide history button in the top bar. arrayOf( - historyMenuItemFingerprint, - historyMenuItemOfflineTabFingerprint - ).forEach { fingerprint -> - fingerprint.method.apply { - val targetIndex = fingerprint.patternMatch!!.startIndex + historyMenuItemMethodMatch, + historyMenuItemOfflineTabMethodMatch, + ).forEach { match -> + match.method.apply { + val targetIndex = match[0] val targetRegister = getInstruction(targetIndex).registerD addInstructions( @@ -81,41 +82,42 @@ val hideButtons = bytecodePatch( """ invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideHistoryButton(Z)Z move-result v$targetRegister - """ + """, ) } } // Region for hide cast, search and notification buttons in the top bar. arrayOf( - Triple(playerOverlayChipFingerprint, playerOverlayChip, "hideCastButton"), - Triple(searchActionViewFingerprint, searchButton, "hideSearchButton"), - Triple(topBarMenuItemImageViewFingerprint, topBarMenuItemImageView, "hideNotificationButton") - ).forEach { (fingerprint, resourceIdLiteral, methodName) -> - fingerprint.method.apply { + Triple(playerOverlayChipMethod, playerOverlayChip, "hideCastButton"), + Triple(searchActionViewMethod, searchButton, "hideSearchButton"), + Triple(topBarMenuItemImageViewMethod, topBarMenuItemImageView, "hideNotificationButton"), + ).forEach { (method, resourceIdLiteral, methodName) -> + method.apply { val resourceIndex = indexOfFirstLiteralInstructionOrThrow(resourceIdLiteral) val targetIndex = indexOfFirstInstructionOrThrow( - resourceIndex, Opcode.MOVE_RESULT_OBJECT + resourceIndex, + Opcode.MOVE_RESULT_OBJECT, ) val targetRegister = getInstruction(targetIndex).registerA addInstruction( targetIndex + 1, "invoke-static { v$targetRegister }, " + - "$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V" + "$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V", ) } } // Region for hide cast button in the player. - mediaRouteButtonFingerprint.classDef.methods.single { method -> - method.name == "setVisibility" + mediaRouteButtonMethod.immutableClassDef.firstMethod { + name == "setVisibility" }.addInstructions( 0, """ invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(I)I move-result p1 - """ + """, ) } } 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/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt index 76e03d2c99..00daa527dc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt @@ -1,16 +1,17 @@ package app.revanced.patches.music.layout.compactheader -import com.android.tools.smali.dexlib2.Opcode -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.literal +import com.android.tools.smali.dexlib2.Opcode -internal val chipCloudFingerprint = fingerprint { - returns("V") +internal val BytecodePatchContext.chipCloudMethodMatch by composingFirstMethod { + returnType("V") opcodes( Opcode.CONST, Opcode.CONST_4, Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT + Opcode.MOVE_RESULT_OBJECT, ) literal { chipCloud } } 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..7bc195419c 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 @@ -1,15 +1,14 @@ package app.revanced.patches.music.layout.compactheader -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.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.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.settings.preference.SwitchPreference import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -19,9 +18,9 @@ internal var chipCloud = -1L private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideCategoryBarPatch;" @Suppress("unused") -val hideCategoryBar = bytecodePatch( +val hideCategoryBarPatch = bytecodePatch( name = "Hide category bar", - description = "Adds an option to hide the category bar at the top of the homepage." + description = "Adds an option to hide the category bar at the top of the homepage.", ) { dependsOn( sharedExtensionPatch, @@ -32,26 +31,26 @@ val hideCategoryBar = bytecodePatch( compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { - chipCloud = resourceMappings["layout", "chip_cloud"] - + apply { addResources("music", "layout.compactheader.hideCategoryBar") PreferenceScreen.GENERAL.addPreferences( SwitchPreference("revanced_music_hide_category_bar"), ) - chipCloudFingerprint.method.apply { - val targetIndex = chipCloudFingerprint.patternMatch!!.endIndex - val targetRegister = getInstruction(targetIndex).registerA + chipCloud = ResourceType.LAYOUT["chip_cloud"] - addInstruction( + chipCloudMethodMatch.let { + val targetIndex = it[-1] + val targetRegister = it.method.getInstruction(targetIndex).registerA + + it.method.addInstruction( targetIndex + 1, - "invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideCategoryBar(Landroid/view/View;)V" + "invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideCategoryBar(Landroid/view/View;)V", ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/hide/general/HideLayoutComponentsPatch.kt index e4da9aafe2..0cf6ad4c90 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/hide/general/HideLayoutComponentsPatch.kt @@ -7,6 +7,6 @@ import app.revanced.patches.shared.layout.hide.general.hideLayoutComponentsPatch val hideLayoutComponentsPatch = hideLayoutComponentsPatch( lithoFilterPatch = lithoFilterPatch, settingsPatch = settingsPatch, - filterClasses = setOf("Lapp/revanced/extension/shared/patches/components/CustomFilter;"), - compatibleWithPackages = arrayOf("com.google.android.apps.youtube.music" to setOf("7.29.52", "8.10.52")) -) \ No newline at end of file + filterClasses = setOf("Lapp/revanced/extension/shared/patches/litho/CustomFilter;"), + compatibleWithPackages = arrayOf("com.google.android.apps.youtube.music" to setOf("7.29.52", "8.10.52")), +) 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..7a699b2675 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 @@ -2,89 +2,89 @@ package app.revanced.patches.music.layout.miniplayercolor -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.accessFlags +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.parameterTypes import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.returnType import app.revanced.patches.all.misc.resources.addResources 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 -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.util.* import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode 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") -val changeMiniplayerColor = bytecodePatch( +val changeMiniplayerColorPatch = bytecodePatch( name = "Change miniplayer color", - description = "Adds an option to change the miniplayer background color to match the fullscreen player." + description = "Adds an option to change the miniplayer background color to match the fullscreen player.", ) { dependsOn( sharedExtensionPatch, settingsPatch, addResourcesPatch, - resourceMappingPatch + resourceMappingPatch, ) compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { - mpp_player_bottom_sheet = resourceMappings["id", "mpp_player_bottom_sheet"] - + apply { addResources("music", "layout.miniplayercolor.changeMiniplayerColor") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_music_change_miniplayer_color"), ) - switchToggleColorFingerprint.match(miniPlayerConstructorFingerprint.classDef).let { - val relativeIndex = it.patternMatch!!.endIndex + 1 + miniPlayerConstructorMethodMatch.immutableClassDef.switchToggleColorMethodMatch.let { + val relativeIndex = it[-1] + 1 val invokeVirtualIndex = it.method.indexOfFirstInstructionOrThrow( - relativeIndex, Opcode.INVOKE_VIRTUAL + relativeIndex, + Opcode.INVOKE_VIRTUAL, ) val colorMathPlayerInvokeVirtualReference = it.method .getInstruction(invokeVirtualIndex).reference val iGetIndex = it.method.indexOfFirstInstructionOrThrow( - relativeIndex, Opcode.IGET + relativeIndex, + Opcode.IGET, ) val colorMathPlayerIGetReference = it.method .getInstruction(iGetIndex).reference as FieldReference - val colorGreyIndex = miniPlayerConstructorFingerprint.method.indexOfFirstInstructionReversedOrThrow { - getReference()?.name == "getColor" - } - val iPutIndex = miniPlayerConstructorFingerprint.method.indexOfFirstInstructionOrThrow( - colorGreyIndex, Opcode.IPUT + val colorGreyIndex = + miniPlayerConstructorMethodMatch.immutableMethod.indexOfFirstInstructionReversedOrThrow { + getReference()?.name == "getColor" + } + val iPutIndex = miniPlayerConstructorMethodMatch.immutableMethod.indexOfFirstInstructionOrThrow( + colorGreyIndex, + Opcode.IPUT, ) - val colorMathPlayerIPutReference = miniPlayerConstructorFingerprint.method + val colorMathPlayerIPutReference = miniPlayerConstructorMethodMatch.immutableMethod .getInstruction(iPutIndex).reference - miniPlayerConstructorFingerprint.classDef.methods.single { method -> - method.accessFlags == AccessFlags.PUBLIC.value or AccessFlags.FINAL.value && - method.returnType == "V" && - method.parameters == it.originalMethod.parameters + miniPlayerConstructorMethodMatch.immutableClassDef.firstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes( + parameterTypePrefixes = it.method.parameterTypes.map { type -> type.toString() } + .toTypedArray(), + ) }.apply { val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.INVOKE_DIRECT) val freeRegister = findFreeRegister(insertIndex) @@ -102,7 +102,7 @@ val changeMiniplayerColor = bytecodePatch( iput v$freeRegister, p0, $colorMathPlayerIPutReference :off nop - """ + """, ) } } 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..5a3ee206e3 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,27 +1,31 @@ package app.revanced.patches.music.layout.miniplayercolor -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val miniPlayerConstructorFingerprint = fingerprint { - returns("V") - strings("sharedToggleMenuItemMutations") - literal { mpp_player_bottom_sheet } +internal val BytecodePatchContext.miniPlayerConstructorMethodMatch by composingFirstMethod { + returnType("V") + instructions( + ResourceType.ID("mpp_player_bottom_sheet"), + "sharedToggleMenuItemMutations"(), + ) } /** - * Matches to the class found in [miniPlayerConstructorFingerprint]. + * Matches to the class found in [miniPlayerConstructorMethodMatch]. */ -internal val switchToggleColorFingerprint = fingerprint { +internal val ClassDef.switchToggleColorMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") - parameters("L", "J") + returnType("V") + parameterTypes("L", "J") opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT, Opcode.CHECK_CAST, - Opcode.IGET + Opcode.IGET, ) } 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..00984d1e01 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 @@ -1,6 +1,7 @@ package app.revanced.patches.music.layout.navigationbar -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.containsLiteralInstruction import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction @@ -9,10 +10,10 @@ 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 tabLayoutTextFingerprint = fingerprint { +internal val BytecodePatchContext.tabLayoutTextMethodMatch by composingFirstMethod("FEmusic_search") { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") + returnType("V") + parameterTypes("L") opcodes( Opcode.IGET, Opcode.INVOKE_STATIC, @@ -20,17 +21,15 @@ internal val tabLayoutTextFingerprint = fingerprint { Opcode.IF_NEZ, Opcode.SGET_OBJECT, Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT + Opcode.MOVE_RESULT, ) - strings("FEmusic_search") - custom { method, _ -> - method.containsLiteralInstruction(text1) - && indexOfGetVisibilityInstruction(method) >= 0 + custom { + containsLiteralInstruction(text1) && + indexOfGetVisibilityInstruction(this) >= 0 } } -internal fun indexOfGetVisibilityInstruction(method: Method) = - method.indexOfFirstInstruction { - opcode == Opcode.INVOKE_VIRTUAL && - getReference()?.name == "getVisibility" - } +internal fun indexOfGetVisibilityInstruction(method: Method) = method.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.name == "getVisibility" +} 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..f977920b34 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 @@ -1,7 +1,7 @@ package app.revanced.patches.music.layout.navigationbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch @@ -10,9 +10,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.ResourceType 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 @@ -30,7 +29,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/pa @Suppress("unused") val navigationBarPatch = bytecodePatch( name = "Navigation bar", - description = "Adds options to hide navigation bar, labels and buttons." + description = "Adds options to hide navigation bar, labels and buttons.", ) { dependsOn( resourceMappingPatch, @@ -38,7 +37,7 @@ val navigationBarPatch = bytecodePatch( settingsPatch, addResourcesPatch, resourcePatch { - execute { + apply { // Ensure the first ImageView has 'layout_weight' to stay properly sized // when the TextView is hidden. document("res/layout/image_with_text_tab.xml").use { document -> @@ -52,18 +51,18 @@ val navigationBarPatch = bytecodePatch( } } } - } + }, ) compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { - text1 = resourceMappings["id", "text1"] + apply { + text1 = ResourceType.ID["text1"] addResources("music", "layout.navigationbar.navigationBarPatch") @@ -80,27 +79,28 @@ val navigationBarPatch = bytecodePatch( SwitchPreference("revanced_music_hide_navigation_bar"), SwitchPreference("revanced_music_hide_navigation_bar_labels"), - ) - ) + ), + ), ) - tabLayoutTextFingerprint.method.apply { + tabLayoutTextMethodMatch.method.apply { // Hide navigation labels. val constIndex = indexOfFirstLiteralInstructionOrThrow(text1) val targetIndex = indexOfFirstInstructionOrThrow(constIndex, Opcode.CHECK_CAST) val targetParameter = getInstruction(targetIndex).reference val targetRegister = getInstruction(targetIndex).registerA - if (!targetParameter.toString().endsWith("Landroid/widget/TextView;")) + if (!targetParameter.toString().endsWith("Landroid/widget/TextView;")) { throw PatchException("Method signature parameter did not match: $targetParameter") + } addInstruction( targetIndex + 1, - "invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationLabel(Landroid/widget/TextView;)V" + "invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationLabel(Landroid/widget/TextView;)V", ) // Set navigation enum and hide navigation buttons. - val enumIndex = tabLayoutTextFingerprint.patternMatch!!.startIndex + 3 + val enumIndex = tabLayoutTextMethodMatch[0] + 3 val enumRegister = getInstruction(enumIndex).registerA val insertEnumIndex = indexOfFirstInstructionOrThrow(Opcode.AND_INT_LIT8) - 2 @@ -109,12 +109,12 @@ val navigationBarPatch = bytecodePatch( addInstruction( pivotTabIndex, - "invoke-static { v$pivotTabRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationButton(Landroid/view/View;)V" + "invoke-static { v$pivotTabRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationButton(Landroid/view/View;)V", ) addInstruction( insertEnumIndex, - "invoke-static { v$enumRegister }, $EXTENSION_CLASS_DESCRIPTOR->setLastAppNavigationEnum(Ljava/lang/Enum;)V" + "invoke-static { v$enumRegister }, $EXTENSION_CLASS_DESCRIPTOR->setLastAppNavigationEnum(Ljava/lang/Enum;)V", ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt index 29558ab4be..cc4e07125a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt @@ -1,24 +1,27 @@ package app.revanced.patches.music.layout.premium -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val hideGetPremiumFingerprint = fingerprint { +internal val BytecodePatchContext.hideGetPremiumMethodMatch by composingFirstMethod( + "FEmusic_history", + "FEmusic_offline", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() + returnType("V") + parameterTypes() opcodes( Opcode.IF_NEZ, Opcode.CONST_16, Opcode.INVOKE_VIRTUAL, ) - strings("FEmusic_history", "FEmusic_offline") } -internal val membershipSettingsFingerprint = fingerprint { +internal val BytecodePatchContext.membershipSettingsMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/CharSequence;") + returnType("Ljava/lang/CharSequence;") opcodes( Opcode.IGET_OBJECT, Opcode.INVOKE_INTERFACE, 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..d6f1c0092d 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 @@ -1,9 +1,9 @@ package app.revanced.patches.music.layout.premium -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -16,7 +16,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideGetPremiumPatch;" @Suppress("unused") -val hideGetPremiumPatch = bytecodePatch( +val hideGetMusicPremiumPatch = bytecodePatch( name = "Hide 'Get Music Premium'", description = "Adds an option to hide the \"Get Music Premium\" label in the settings and account menu.", ) { @@ -29,37 +29,39 @@ val hideGetPremiumPatch = bytecodePatch( compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { + apply { addResources("music", "layout.premium.hideGetPremiumPatch") PreferenceScreen.ADS.addPreferences( SwitchPreference("revanced_music_hide_get_premium_label"), ) - hideGetPremiumFingerprint.method.apply { - val insertIndex = hideGetPremiumFingerprint.patternMatch!!.endIndex + hideGetPremiumMethodMatch.let { + val insertIndex = it[-1] - val setVisibilityInstruction = getInstruction(insertIndex) - val getPremiumViewRegister = setVisibilityInstruction.registerC - val visibilityRegister = setVisibilityInstruction.registerD + it.method.apply { + val setVisibilityInstruction = getInstruction(insertIndex) + val getPremiumViewRegister = setVisibilityInstruction.registerC + val visibilityRegister = setVisibilityInstruction.registerD - replaceInstruction( - insertIndex, - "const/16 v$visibilityRegister, 0x8", - ) + replaceInstruction( + insertIndex, + "const/16 v$visibilityRegister, 0x8", + ) - addInstruction( - insertIndex + 1, - "invoke-virtual {v$getPremiumViewRegister, v$visibilityRegister}, " + - "Landroid/view/View;->setVisibility(I)V", - ) + addInstruction( + insertIndex + 1, + "invoke-virtual {v$getPremiumViewRegister, v$visibilityRegister}, " + + "Landroid/view/View;->setVisibility(I)V", + ) + } } - membershipSettingsFingerprint.method.addInstructionsWithLabels( + membershipSettingsMethod.addInstructionsWithLabels( 0, """ invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->hideGetPremiumLabel()Z @@ -69,7 +71,7 @@ val hideGetPremiumPatch = bytecodePatch( return-object v0 :show nop - """ + """, ) } } 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/androidauto/BypassCertificateChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt deleted file mode 100644 index 36a91466a1..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt +++ /dev/null @@ -1,15 +0,0 @@ -package app.revanced.patches.music.misc.androidauto - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.music.misc.extension.sharedExtensionPatch -import app.revanced.patches.music.misc.settings.settingsPatch -import app.revanced.util.returnEarly - -@Deprecated("This patch is useless by itself and has been merged into another patch.", ReplaceWith("unlockAndroidAutoMediaBrowserPatch")) - -@Suppress("unused") -val bypassCertificateChecksPatch = bytecodePatch( - description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.", -) { - dependsOn(unlockAndroidAutoMediaBrowserPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt index 63c9de464d..6fb1748eac 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt @@ -1,21 +1,29 @@ package app.revanced.patches.music.misc.androidauto -import app.revanced.patcher.fingerprint +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val checkCertificateFingerprint = fingerprint { - returns("Z") - parameters("Ljava/lang/String;") - strings( - "X509", - "Failed to get certificate" // Partial String match. - ) +internal val BytecodePatchContext.checkCertificateMethod by gettingFirstMethodDeclaratively( + "X509", +) { + returnType("Z") + parameterTypes("Ljava/lang/String;") + instructions("Failed to get certificate"(String::contains)) } -internal val searchMediaItemsConstructorFingerprint = fingerprint { - returns("V") - strings("ytm_media_browser/search_media_items") +internal val BytecodePatchContext.searchMediaItemsConstructorMethod by gettingFirstMethodDeclaratively( + "ytm_media_browser/search_media_items", +) { + returnType("V") } -internal val searchMediaItemsExecuteFingerprint = fingerprint { - parameters() -} \ No newline at end of file +context(_: BytecodePatchContext) +internal fun ClassDef.getSearchMediaItemsExecuteMethod() = firstMethodDeclaratively { + parameterTypes() +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/UnlockAndroidAutoMediaBrowserPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/UnlockAndroidAutoMediaBrowserPatch.kt index 55fb2bb12e..dd2b2eaac7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/UnlockAndroidAutoMediaBrowserPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/UnlockAndroidAutoMediaBrowserPatch.kt @@ -1,13 +1,13 @@ package app.revanced.patches.music.misc.androidauto -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.fieldReference +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.getReference import app.revanced.util.registersUsed import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.FieldReference @Suppress("unused") val unlockAndroidAutoMediaBrowserPatch = bytecodePatch( @@ -17,18 +17,16 @@ val unlockAndroidAutoMediaBrowserPatch = bytecodePatch( compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { - checkCertificateFingerprint.method.returnEarly(true) + apply { + checkCertificateMethod.returnEarly(true) - searchMediaItemsExecuteFingerprint - .match(searchMediaItemsConstructorFingerprint.classDef) - .method.apply { - val targetIndex = instructions.indexOfFirst { - it.opcode == Opcode.IGET_OBJECT && it.getReference()?.type == "Ljava/lang/String;" + searchMediaItemsConstructorMethod.immutableClassDef.getSearchMediaItemsExecuteMethod().apply { + val targetIndex = instructions.indexOfFirst { + it.opcode == Opcode.IGET_OBJECT && it.fieldReference?.type == "Ljava/lang/String;" } val register = instructions[targetIndex].registersUsed.first() diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt index bf35b24d5d..5d8b651c18 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -1,33 +1,29 @@ package app.revanced.patches.music.misc.backgroundplayback -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.settings.settingsPatch import app.revanced.util.returnEarly -val backgroundPlaybackPatch = bytecodePatch( +@Suppress("unused") +val removeBackgroundPlaybackRestrictionsPatch = bytecodePatch( name = "Remove background playback restrictions", description = "Removes restrictions on background playback, including playing kids videos in the background.", ) { dependsOn( sharedExtensionPatch, - settingsPatch + settingsPatch, ) compatibleWith( "com.google.android.apps.youtube.music"( "7.29.52", - "8.10.52" - ) + "8.10.52", + ), ) - execute { - kidsBackgroundPlaybackPolicyControllerFingerprint.method.addInstruction( - 0, - "return-void", - ) - - backgroundPlaybackDisableFingerprint.method.returnEarly(true) + apply { + kidsBackgroundPlaybackPolicyControllerMethod.returnEarly() + backgroundPlaybackDisableMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt index e1cf24e1a2..fe6481c303 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt @@ -1,13 +1,14 @@ package app.revanced.patches.music.misc.backgroundplayback -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val backgroundPlaybackDisableFingerprint = fingerprint { +internal val BytecodePatchContext.backgroundPlaybackDisableMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("L") + returnType("Z") + parameterTypes("L") opcodes( Opcode.CONST_4, Opcode.IF_EQZ, @@ -21,10 +22,10 @@ internal val backgroundPlaybackDisableFingerprint = fingerprint { ) } -internal val kidsBackgroundPlaybackPolicyControllerFingerprint = fingerprint { +internal val BytecodePatchContext.kidsBackgroundPlaybackPolicyControllerMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("I", "L", "Z") + returnType("V") + parameterTypes("I", "L", "Z") opcodes( Opcode.IGET, Opcode.IF_NE, diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt index d6ac3ef780..aa00a581ab 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.music.misc.dns import app.revanced.patches.music.misc.extension.sharedExtensionPatch -import app.revanced.patches.music.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.music.shared.mainActivityOnCreateMethod import app.revanced.patches.shared.misc.dns.checkWatchHistoryDomainNameResolutionPatch val checkWatchHistoryDomainNameResolutionPatch = checkWatchHistoryDomainNameResolutionPatch( @@ -18,5 +18,5 @@ val checkWatchHistoryDomainNameResolutionPatch = checkWatchHistoryDomainNameReso ) }, - mainActivityFingerprint = mainActivityOnCreateFingerprint + getMainActivityMethod = { mainActivityOnCreateMethod } ) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt index b10c321a48..1409c8f57e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt @@ -6,5 +6,6 @@ import app.revanced.patches.shared.misc.extension.sharedExtensionPatch val sharedExtensionPatch = sharedExtensionPatch( "music", - applicationInitHook, applicationInitOnCreateHook + applicationInitHook, + applicationInitOnCreateHook ) 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..2fefbb7833 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.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.returnType 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") - custom { method, _ -> method.name == "onCreate" } + name("onCreate") + returnType("V") + parameterTypes() + instructions("activity"()) } -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/fileprovider/FileProviderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/fileprovider/FileProviderPatch.kt index 32c162a46f..9c36f4afa3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/fileprovider/FileProviderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/fileprovider/FileProviderPatch.kt @@ -1,9 +1,8 @@ package app.revanced.patches.music.misc.fileprovider -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.addInstructionsWithLabels import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName -import app.revanced.patches.music.utils.fix.fileprovider.fileProviderResolverFingerprint internal fun fileProviderPatch( youtubePackageName: String, @@ -11,7 +10,7 @@ internal fun fileProviderPatch( ) = bytecodePatch( description = "Fixes broken YouTube Music file provider that prevents sharing with specific apps such as Instagram." ) { - finalize { + afterDependents { // Must do modification last, so change package name value is correctly set. val musicChangedPackageName = setOrGetFallbackPackageName(musicPackageName) @@ -22,7 +21,7 @@ internal fun fileProviderPatch( // https://github.com/ReVanced/revanced-patches/issues/55 // // To solve this issue, replace the package name of YouTube with YT Music's package name. - fileProviderResolverFingerprint.method.addInstructionsWithLabels( + fileProviderResolverMethod.addInstructionsWithLabels( 0, """ const-string v0, "com.google.android.youtube.fileprovider" diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/fileprovider/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/fileprovider/Fingerprints.kt index 16a2d6a58f..d4682eaa9b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/fileprovider/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/fileprovider/Fingerprints.kt @@ -1,11 +1,12 @@ -package app.revanced.patches.music.utils.fix.fileprovider +package app.revanced.patches.music.misc.fileprovider -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val fileProviderResolverFingerprint = fingerprint { - returns("L") - strings( - "android.support.FILE_PROVIDER_PATHS", - "Name must not be empty" - ) +internal val BytecodePatchContext.fileProviderResolverMethod by gettingFirstMethodDeclaratively( + "android.support.FILE_PROVIDER_PATHS", + "Name must not be empty" +) { + returnType("L") } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt index 7131e143df..faa6de9ff6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt @@ -1,11 +1,11 @@ package app.revanced.patches.music.misc.gms -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val musicActivityOnCreateFingerprint = fingerprint { - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - method.name == "onCreate" && classDef.endsWith("/MusicActivity;") - } +internal val BytecodePatchContext.musicActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("/MusicActivity;") + returnType("V") + parameterTypes("Landroid/os/Bundle;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt index 5d74ef8be9..88cfe2ca6f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt @@ -1,5 +1,6 @@ package app.revanced.patches.music.misc.gms +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.Option import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -9,27 +10,23 @@ import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.settingsPatch -import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch -import app.revanced.patches.shared.castContextFetchFingerprint +import app.revanced.patches.shared.castContextFetchMethod import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch import app.revanced.patches.shared.misc.settings.preference.IntentPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.shared.primeMethodFingerprint +import app.revanced.patches.shared.primeMethod @Suppress("unused") val gmsCoreSupportPatch = gmsCoreSupportPatch( fromPackageName = MUSIC_PACKAGE_NAME, toPackageName = REVANCED_MUSIC_PACKAGE_NAME, - primeMethodFingerprint = primeMethodFingerprint, - earlyReturnFingerprints = setOf( - castContextFetchFingerprint, - ), - mainActivityOnCreateFingerprintToInsertIndex = musicActivityOnCreateFingerprint to { 0 }, + getPrimeMethod = { primeMethod }, + getEarlyReturnMethods = setOf(BytecodePatchContext::castContextFetchMethod::get), + getMainActivityOnCreateMethodToGetInsertIndex = BytecodePatchContext::musicActivityOnCreateMethod::get to { 0 }, extensionPatch = sharedExtensionPatch, gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch, ) { - dependsOn(spoofVideoStreamsPatch) compatibleWith( MUSIC_PACKAGE_NAME( diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt index fb92579992..ebb0edc8a6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt @@ -1,8 +1,12 @@ package app.revanced.patches.music.misc.litho.filter +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patches.music.misc.extension.sharedExtensionPatch -import app.revanced.patches.music.shared.conversionContextFingerprintToString +import app.revanced.patches.music.shared.conversionContextToStringMethod +import app.revanced.patches.shared.misc.litho.filter.EXTENSION_CLASS_DESCRIPTOR import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceLegacyMethod import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode @@ -11,7 +15,13 @@ val lithoFilterPatch = lithoFilterPatch( // No supported version clobbers p2 so we can just do our things before the return instruction. indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) }, - conversionContextFingerprintToString = conversionContextFingerprintToString, + getConversionContextToStringMethod = BytecodePatchContext::conversionContextToStringMethod::get, + insertProtobufHook = { + protobufBufferReferenceLegacyMethod.addInstruction( + 0, + "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", + ) + }, ) { dependsOn(sharedExtensionPatch) -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/Fingerprints.kt index 580e6e7679..5774481c8f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/settings/Fingerprints.kt @@ -1,11 +1,15 @@ package app.revanced.patches.music.misc.settings -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val googleApiActivityFingerprint = fingerprint { - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - classDef.endsWith("GoogleApiActivity;") && method.name == "onCreate" - } +internal val BytecodePatchContext.googleApiActivityMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("GoogleApiActivity;") + returnType("V") + parameterTypes("Landroid/os/Bundle;") } 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..497e100112 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 @@ -1,5 +1,6 @@ package app.revanced.patches.music.misc.settings +import app.revanced.patcher.classDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName @@ -8,14 +9,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME import app.revanced.patches.shared.misc.mapping.resourceMappingPatch -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.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.shared.misc.settings.preference.* import app.revanced.patches.shared.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.modifyActivityForSettingsInjection import app.revanced.util.copyXmlNode @@ -30,18 +24,18 @@ 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 ) ) - execute { + apply { // Set the style for the ReVanced settings to follow the style of the music settings, // namely: action bar height, menu item padding and remove horizontal dividers. @@ -72,7 +66,7 @@ private val settingsResourcePatch = resourcePatch { } val settingsPatch = bytecodePatch( - description = "Adds settings for ReVanced to YouTube Music.", + description = "Adds settings for ReVanced to YouTube Music." ) { dependsOn( sharedExtensionPatch, @@ -80,7 +74,7 @@ val settingsPatch = bytecodePatch( addResourcesPatch, ) - execute { + apply { addResources("music", "misc.settings.settingsPatch") addResources("shared", "misc.debugging.enableDebuggingPatch") @@ -107,14 +101,14 @@ val settingsPatch = bytecodePatch( ) modifyActivityForSettingsInjection( - googleApiActivityFingerprint.classDef, - googleApiActivityFingerprint.method, + googleApiActivityMethod.classDef, + googleApiActivityMethod, GOOGLE_API_ACTIVITY_HOOK_CLASS_DESCRIPTOR, true ) } - finalize { + afterDependents { PreferenceScreen.close() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt index e4268330de..f019fca848 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt @@ -3,14 +3,10 @@ package app.revanced.patches.music.misc.spoof import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch -import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint +import app.revanced.patches.music.misc.gms.musicActivityOnCreateMethod import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.settingsPatch -import app.revanced.patches.music.playservice.is_7_16_or_greater -import app.revanced.patches.music.playservice.is_7_33_or_greater -import app.revanced.patches.music.playservice.is_8_11_or_greater -import app.revanced.patches.music.playservice.is_8_15_or_greater -import app.revanced.patches.music.playservice.versionCheckPatch +import app.revanced.patches.music.playservice.* import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference @@ -18,7 +14,7 @@ import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch val spoofVideoStreamsPatch = spoofVideoStreamsPatch( extensionClassDescriptor = "Lapp/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch;", - mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint, + getMainActivityOnCreateMethod = { musicActivityOnCreateMethod }, fixMediaFetchHotConfig = { is_7_16_or_greater }, fixMediaFetchHotConfigAlternative = { is_8_11_or_greater && !is_8_15_or_greater }, fixParsePlaybackResponseFeatureFlag = { is_7_33_or_greater }, diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/tracks/ForceOriginalAudioPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/tracks/ForceOriginalAudioPatch.kt index 09be85c565..cbdc7ab803 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/tracks/ForceOriginalAudioPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/tracks/ForceOriginalAudioPatch.kt @@ -5,7 +5,7 @@ import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.settingsPatch import app.revanced.patches.music.playservice.is_8_05_or_greater import app.revanced.patches.music.playservice.versionCheckPatch -import app.revanced.patches.music.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.music.shared.mainActivityOnCreateMethod import app.revanced.patches.shared.misc.audio.forceOriginalAudioPatch @Suppress("unused") @@ -25,7 +25,7 @@ val forceOriginalAudioPatch = forceOriginalAudioPatch( ) }, fixUseLocalizedAudioTrackFlag = { is_8_05_or_greater }, - mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + getMainActivityOnCreateMethod = { mainActivityOnCreateMethod }, subclassExtensionClassDescriptor = "Lapp/revanced/extension/music/patches/ForceOriginalAudioPatch;", preferenceScreen = PreferenceScreen.MISC, ) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/playservice/VersionCheckPatch.kt index d8f1fd7096..134121b677 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/playservice/VersionCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/playservice/VersionCheckPatch.kt @@ -21,12 +21,14 @@ var is_8_11_or_greater: Boolean by Delegates.notNull() var is_8_15_or_greater: Boolean by Delegates.notNull() private set +@Suppress("unused") val versionCheckPatch = resourcePatch( - description = "Uses the Play Store service version to find the major/minor version of the YouTube Music target app.", + description = "Uses the Play Store service version to find the major/minor version of the YouTube Music target app." ) { - execute { + apply { // The app version is missing from the decompiled manifest, // so instead use the Google Play services version and compare against specific releases. + // This requires ResourcePatchContext, which creatingResourcePatch provides. val playStoreServicesVersion = findPlayStoreServicesVersion() // All bug fix releases always seem to use the same play store version as the minor version. diff --git a/patches/src/main/kotlin/app/revanced/patches/music/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/shared/Fingerprints.kt index f6e7da942b..677831ecce 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/shared/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/shared/Fingerprints.kt @@ -1,29 +1,34 @@ package app.revanced.patches.music.shared -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal const val YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE = "Lcom/google/android/apps/youtube/music/activities/MusicActivity;" +internal const val YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE = + "Lcom/google/android/apps/youtube/music/activities/MusicActivity;" -internal val mainActivityOnCreateFingerprint = fingerprint { - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - method.name == "onCreate" && classDef.type == YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE - } +internal val BytecodePatchContext.mainActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass(YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE) + returnType("V") + parameterTypes("Landroid/os/Bundle;") } -internal val conversionContextFingerprintToString = fingerprint { - parameters() - strings( - "ConversionContext{containerInternal=", - ", gridColumnCount=", - ", gridColumnIndex=", - ", templateLoggerFactory=", - ", rootDisposableContainer=", - ", elementId=", - ", identifierProperty=" - ) - custom { method, _ -> - method.name == "toString" - } +internal val BytecodePatchContext.conversionContextToStringMethod by gettingFirstImmutableMethodDeclaratively( + "ConversionContext{containerInternal=", + ", gridColumnCount=", + ", gridColumnIndex=", + ", templateLoggerFactory=", + ", rootDisposableContainer=", + ", elementId=", + ", identifierProperty=", +) { + name("toString") + parameterTypes() } diff --git a/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt index 6bc4c21e5c..4fd4a691a1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt @@ -1,8 +1,9 @@ package app.revanced.patches.myexpenses.misc.pro -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val isEnabledFingerprint = fingerprint { - returns("Z") - strings("feature", "feature.licenceStatus") +internal val BytecodePatchContext.isEnabledMethod by gettingFirstMethodDeclaratively("feature", "feature.licenceStatus") { + returnType("Z") } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt index e8720d7121..27fed2fb13 100644 --- a/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt @@ -1,21 +1,13 @@ package app.revanced.patches.myexpenses.misc.pro -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val unlockProPatch = bytecodePatch( - name = "Unlock pro", -) { +val unlockProPatch = bytecodePatch("Unlock pro") { compatibleWith("org.totschnig.myexpenses"("3.4.9")) - execute { - isEnabledFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + isEnabledMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt index 160e2db274..6fbc1d380a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt @@ -1,19 +1,22 @@ package app.revanced.patches.myfitnesspal.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val isPremiumUseCaseImplFingerprint = fingerprint { +internal val BytecodePatchContext.isPremiumUseCaseImplMethod by gettingFirstMethodDeclaratively { + name("doWork") + definingClass("IsPremiumUseCaseImpl;") accessFlags(AccessFlags.PUBLIC) - custom { method, classDef -> - classDef.endsWith("IsPremiumUseCaseImpl;") && method.name == "doWork" - } } -internal val mainActivityNavigateToNativePremiumUpsellFingerprint = fingerprint { +internal val BytecodePatchContext.mainActivityNavigateToNativePremiumUpsellMethod by gettingFirstMethodDeclaratively { + name("navigateToNativePremiumUpsell") + definingClass("MainActivity;") accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") - custom { method, classDef -> - classDef.endsWith("MainActivity;") && method.name == "navigateToNativePremiumUpsell" - } + returnType("V") } diff --git a/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt index a9428b29ee..8df70dbcd3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.myfitnesspal.ads -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.extensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,9 +10,9 @@ val hideAdsPatch = bytecodePatch( ) { compatibleWith("com.myfitnesspal.android"("24.14.2")) - execute { + apply { // Overwrite the premium status specifically for ads. - isPremiumUseCaseImplFingerprint.method.replaceInstructions( + isPremiumUseCaseImplMethod.replaceInstructions( 0, """ sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean; @@ -22,7 +22,7 @@ val hideAdsPatch = bytecodePatch( // Prevent the premium upsell dialog from showing when the main activity is launched. // In other places that are premium-only the dialog will still show. - mainActivityNavigateToNativePremiumUpsellFingerprint.method.replaceInstructions( + mainActivityNavigateToNativePremiumUpsellMethod.replaceInstructions( 0, "return-void", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt index b4f675ed74..a7e6912e06 100644 --- a/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt @@ -10,7 +10,7 @@ val removeBroadcastsRestrictionPatch = resourcePatch( ) { compatibleWith("eu.faircode.netguard") - execute { + apply { document("AndroidManifest.xml").use { document -> val applicationNode = document 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/nothingx/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/extension/SharedExtensionPatch.kt index 5f1f78a63e..13b582a69d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/extension/SharedExtensionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/extension/SharedExtensionPatch.kt @@ -1,13 +1,14 @@ package app.revanced.patches.nothingx.misc.extension -import app.revanced.patches.shared.misc.extension.sharedExtensionPatch +import app.revanced.patcher.definingClass +import app.revanced.patcher.name import app.revanced.patches.shared.misc.extension.extensionHook +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch val sharedExtensionPatch = sharedExtensionPatch( extensionName = "nothingx", extensionHook { - custom { method, classDef -> - method.name == "onCreate" && classDef.contains("BaseApplication") - } + name("onCreate") + definingClass("BaseApplication") }, -) \ No newline at end of file +) diff --git a/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/Fingerprints.kt index 84e5d2c2a8..9ad284839d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/Fingerprints.kt @@ -1,16 +1,19 @@ package app.revanced.patches.nothingx.misc.logk1token -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType /** * Fingerprint for the Application onCreate method. * This is used to trigger scanning for existing log files on app startup. */ -internal val applicationOnCreateFingerprint = fingerprint { - returns("V") - parameters() - custom { method, classDef -> - // Match BaseApplication onCreate specifically - method.name == "onCreate" && classDef.endsWith("BaseApplication;") - } +internal val BytecodePatchContext.applicationOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("BaseApplication;") + returnType("V") + parameterTypes() } diff --git a/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/ShowK1TokenPatchs.kt b/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/ShowK1TokenPatchs.kt index 3fd2b9d491..249648b0f1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/ShowK1TokenPatchs.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nothingx/misc/logk1token/ShowK1TokenPatchs.kt @@ -1,6 +1,6 @@ package app.revanced.patches.nothingx.misc.logk1token -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.nothingx.misc.extension.sharedExtensionPatch @@ -11,19 +11,19 @@ private const val EXTENSION_CLASS_DESCRIPTOR = val showK1TokensPatch = bytecodePatch( name = "Show K1 token(s)", description = "Shows the K1 authentication token(s) in a dialog and logs it to logcat " + - "for pairing with GadgetBridge without requiring root access. " + - "After installing this patch, pair your watch with the Nothing X app and " + - "use the token from the dialog or logcat.", + "for pairing with GadgetBridge without requiring root access. " + + "After installing this patch, pair your watch with the Nothing X app and " + + "use the token from the dialog or logcat.", ) { dependsOn(sharedExtensionPatch) compatibleWith("com.nothing.smartcenter"()) - execute { + apply { // Hook Application.onCreate to get K1 tokens from database and log files. // This will find K1 tokens that were already written to log files. // p0 is the Application context in onCreate. - applicationOnCreateFingerprint.method.addInstruction( + applicationOnCreateMethod.addInstruction( 0, "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->showK1Tokens(Landroid/content/Context;)V", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Fingerprints.kt index 109b973e0c..ab57da8c9a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nunl/ads/Fingerprints.kt @@ -1,44 +1,41 @@ package app.revanced.patches.nunl.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -internal val jwPlayerConfigFingerprint = fingerprint { +import com.android.tools.smali.dexlib2.Opcode val BytecodePatchContext.jwPlayerConfigMethod by gettingFirstMethodDeclaratively { + name("advertisingConfig") + definingClass($$"Lcom/jwplayer/pub/api/configuration/PlayerConfig$Builder;") accessFlags(AccessFlags.PUBLIC) - custom { methodDef, classDef -> - classDef.type == "Lcom/jwplayer/pub/api/configuration/PlayerConfig${'$'}Builder;" && methodDef.name == "advertisingConfig" - } } -internal val screenMapperFingerprint = fingerprint { +internal val BytecodePatchContext.screenMapperMethodMatch by composingFirstMethod { + name("map") + definingClass("Lnl/nu/android/bff/data/mappers/ScreenMapper;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Lnl/nu/android/bff/domain/models/screen/ScreenEntity;") - parameters("Lnl/nu/performance/api/client/objects/Screen;") - + returnType("Lnl/nu/android/bff/domain/models/screen/ScreenEntity;") + parameterTypes("Lnl/nu/performance/api/client/objects/Screen;") opcodes( Opcode.MOVE_RESULT_OBJECT, Opcode.IF_EQZ, - Opcode.CHECK_CAST + Opcode.CHECK_CAST, ) - - custom { methodDef, classDef -> - classDef.type == "Lnl/nu/android/bff/data/mappers/ScreenMapper;" && methodDef.name == "map" - } } -internal val nextPageRepositoryImplFingerprint = fingerprint { +internal val BytecodePatchContext.nextPageRepositoryImplMethodMatch by composingFirstMethod { + definingClass("Lnl/nu/android/bff/data/repositories/NextPageRepositoryImpl;") + name("mapToPage") accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("Lnl/nu/android/bff/domain/models/Page;") - parameters("Lnl/nu/performance/api/client/PacResponse;", "Ljava/lang/String;") - + returnType("Lnl/nu/android/bff/domain/models/Page;") + parameterTypes("Lnl/nu/performance/api/client/PacResponse;", "Ljava/lang/String;") opcodes( Opcode.MOVE_RESULT_OBJECT, Opcode.IF_EQZ, - Opcode.CHECK_CAST + Opcode.CHECK_CAST, ) - - custom { methodDef, classDef -> - classDef.type == "Lnl/nu/android/bff/data/repositories/NextPageRepositoryImpl;" && methodDef.name == "mapToPage" - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/nunl/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/nunl/ads/HideAdsPatch.kt index 14d05cfbc2..b2eea685c7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nunl/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nunl/ads/HideAdsPatch.kt @@ -1,15 +1,15 @@ package app.revanced.patches.nunl.ads -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.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.removeInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.shared.misc.extension.sharedExtensionPatch import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -@Suppress("unused") +@Suppress("ObjectPropertyName") val hideAdsPatch = bytecodePatch( name = "Hide ads", description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.", @@ -18,29 +18,27 @@ val hideAdsPatch = bytecodePatch( dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook)) - execute { + apply { // Disable video pre-roll ads. // Whenever the app tries to define the advertising config for JWPlayer, don't set the advertising config and directly return. - val iputInstructionIndex = jwPlayerConfigFingerprint.method.indexOfFirstInstructionOrThrow(Opcode.IPUT_OBJECT) - jwPlayerConfigFingerprint.method.removeInstructions(iputInstructionIndex, 1) + val iputInstructionIndex = jwPlayerConfigMethod.indexOfFirstInstructionOrThrow(Opcode.IPUT_OBJECT) + jwPlayerConfigMethod.removeInstructions(iputInstructionIndex, 1) // Filter injected content from API calls out of lists. - arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach { + arrayOf(screenMapperMethodMatch, nextPageRepositoryImplMethodMatch).forEach { match -> // Index of instruction moving result of BlockPage;->getBlocks(...). - val moveGetBlocksResultObjectIndex = it.patternMatch!!.startIndex - it.method.apply { - val moveInstruction = getInstruction(moveGetBlocksResultObjectIndex) + val moveGetBlocksResultObjectIndex = match[0] + val moveInstruction = match.method.getInstruction(moveGetBlocksResultObjectIndex) - val listRegister = moveInstruction.registerA + val listRegister = moveInstruction.registerA - // Add instruction after moving List to register and then filter this List in place. - addInstructions( - moveGetBlocksResultObjectIndex + 1, - """ + // Add instruction after moving List to register and then filter this List in place. + match.method.addInstructions( + moveGetBlocksResultObjectIndex + 1, + """ invoke-static { v$listRegister }, Lapp/revanced/extension/nunl/ads/HideAdsPatch;->filterAds(Ljava/util/List;)V """, - ) - } + ) } } } 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/nunl/firebase/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/nunl/firebase/Fingerprints.kt index 490819a4b3..ff01d0d8e2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nunl/firebase/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nunl/firebase/Fingerprints.kt @@ -1,20 +1,20 @@ package app.revanced.patches.nunl.firebase -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val getFingerprintHashForPackageFingerprints = arrayOf( +internal fun BytecodePatchContext.getFingerprintHashForPackageMethods() = arrayOf( "Lcom/google/firebase/installations/remote/FirebaseInstallationServiceClient;", "Lcom/google/firebase/remoteconfig/internal/ConfigFetchHttpClient;", "Lcom/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient;" -).map { className -> - fingerprint { +).map { + firstMethodDeclaratively { + name("getFingerprintHashForPackage") + definingClass(it) accessFlags(AccessFlags.PRIVATE) - parameters() - returns("Ljava/lang/String;") - - custom { methodDef, classDef -> - classDef.type == className && methodDef.name == "getFingerprintHashForPackage" - } + returnType("Ljava/lang/String;") + parameterTypes() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/nunl/firebase/SpoofCertificatePatch.kt b/patches/src/main/kotlin/app/revanced/patches/nunl/firebase/SpoofCertificatePatch.kt index 65944c4c1f..f53524bab0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/nunl/firebase/SpoofCertificatePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/nunl/firebase/SpoofCertificatePatch.kt @@ -10,9 +10,9 @@ val spoofCertificatePatch = bytecodePatch( ) { compatibleWith("nl.sanomamedia.android.nu") - execute { - getFingerprintHashForPackageFingerprints.forEach { fingerprint -> - fingerprint.method.returnEarly("eae41fc018df2731a9b6ae1ac327da44a288667b") + apply { + getFingerprintHashForPackageMethods().forEach { + it.returnEarly("eae41fc018df2731a9b6ae1ac327da44a288667b") } } } 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/openinghours/misc/fix/crash/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt index 69463c510d..09881cc986 100644 --- a/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt @@ -1,12 +1,15 @@ package app.revanced.patches.openinghours.misc.fix.crash -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.definingClass +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val setPlaceFingerprint = fingerprint { - returns("V") - parameters("Lde/simon/openinghours/models/Place;") - custom { method, _ -> - method.name == "setPlace" && - method.definingClass == "Lde/simon/openinghours/views/custom/PlaceCard;" - } +internal val BytecodePatchContext.setPlaceMethod by gettingFirstMethodDeclaratively { + name("setPlace") + definingClass("Lde/simon/openinghours/views/custom/PlaceCard;") + returnType("V") + parameterTypes("Lde/simon/openinghours/models/Place;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt index db38e6161e..d2242b172c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.openinghours.misc.fix.crash -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.extensions.newLabel +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.getReference import com.android.tools.smali.dexlib2.Opcode @@ -14,11 +14,12 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Suppress("unused") val fixCrashPatch = bytecodePatch( name = "Fix crash", + description = "Fixes a crash when opening a place.", ) { compatibleWith("de.simon.openinghours"("1.0")) - execute { - val indexedInstructions = setPlaceFingerprint.method.instructions.withIndex().toList() + apply { + val indexedInstructions = setPlaceMethod.instructions.withIndex().toList() /** * This function replaces all `checkNotNull` instructions in the integer interval @@ -27,7 +28,7 @@ val fixCrashPatch = bytecodePatch( * the value is indeed null, we jump to a newly created label at `endIndex + 1`. */ fun avoidNullPointerException(startIndex: Int, endIndex: Int) { - val continueLabel = setPlaceFingerprint.method.newLabel(endIndex + 1) + val continueLabel = setPlaceMethod.newLabel(endIndex + 1) for (index in startIndex..endIndex) { val instruction = indexedInstructions[index].value @@ -39,7 +40,7 @@ val fixCrashPatch = bytecodePatch( val checkNotNullInstruction = instruction as FiveRegisterInstruction val originalRegister = checkNotNullInstruction.registerC - setPlaceFingerprint.method.replaceInstruction( + setPlaceMethod.replaceInstruction( index, BuilderInstruction21t( Opcode.IF_EQZ, diff --git a/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/Fingerprints.kt index 525d4f37d2..61e98df83b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/Fingerprints.kt @@ -1,13 +1,16 @@ package app.revanced.patches.orfon.detection.root -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val isDeviceRootedFingeprint = fingerprint { +internal val BytecodePatchContext.isDeviceRootedMethod by gettingFirstMethodDeclaratively { + name("isDeviceRooted") + definingClass("/RootChecker;") accessFlags(AccessFlags.PUBLIC) - returns("Z") - custom { method, classDef -> - method.name == "isDeviceRooted" && - classDef.endsWith("/RootChecker;") - } + returnType("Z") } diff --git a/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/RemoveRootDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/RemoveRootDetectionPatch.kt index 8669f6132b..2621021057 100644 --- a/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/RemoveRootDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/orfon/detection/root/RemoveRootDetectionPatch.kt @@ -1,18 +1,16 @@ package app.revanced.patches.orfon.detection.root import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION -import app.revanced.patches.shared.PATCH_NAME_REMOVE_ROOT_DETECTION import app.revanced.util.returnEarly @Suppress("unused") val removeRootDetectionPatch = bytecodePatch( - name = PATCH_NAME_REMOVE_ROOT_DETECTION, - description = PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION + name = "Remove root detection", + description = "Removes the check for root permissions and unlocked bootloader.", ) { compatibleWith("com.nousguide.android.orftvthek") - execute { - isDeviceRootedFingeprint.method.returnEarly(false) + apply { + isDeviceRootedMethod.returnEarly(false) } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/pandora/ads/DisableAudioAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/pandora/ads/DisableAudioAdsPatch.kt index 6cc89a0b46..79dd9358a8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/pandora/ads/DisableAudioAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/pandora/ads/DisableAudioAdsPatch.kt @@ -4,13 +4,11 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val disableAudioAdsPatch = bytecodePatch( - name = "Disable audio ads", -) { +val disableAudioAdsPatch = bytecodePatch("Disable Audio Ads") { compatibleWith("com.pandora.android") - execute { - getIsAdSupportedFingerprint.method.returnEarly(false) - requestAudioAdFingerprint.method.returnEarly() + apply { + getIsAdSupportedMethod.returnEarly(false) + requestAudioAdMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/pandora/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/pandora/ads/Fingerprints.kt index e3f432e16d..cbedd3bdb0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/pandora/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/pandora/ads/Fingerprints.kt @@ -1,15 +1,16 @@ package app.revanced.patches.pandora.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val getIsAdSupportedFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getIsAdSupported" && classDef.endsWith("UserData;") - } +internal val BytecodePatchContext.getIsAdSupportedMethod by gettingFirstMethodDeclaratively { + name("getIsAdSupported") + definingClass("UserData;") } -internal val requestAudioAdFingerprint = fingerprint { - custom { method, classDef -> - method.name == "requestAudioAdFromAdSDK" && classDef.endsWith("ContentServiceOpsImpl;") - } -} \ No newline at end of file +internal val BytecodePatchContext.requestAudioAdMethod by gettingFirstMethodDeclaratively { + name("requestAudioAdFromAdSDK") + definingClass("ContentServiceOpsImpl;") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatch.kt index 2ce53fdfee..c63bae4d2f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatch.kt @@ -4,12 +4,10 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val enableUnlimitedSkipsPatch = bytecodePatch( - name = "Enable unlimited skips", -) { +val enableUnlimitedSkipsPatch = bytecodePatch("Enable unlimited skips") { compatibleWith("com.pandora.android") - execute { - skipLimitBehaviorFingerprint.method.returnEarly("unlimited") + apply { + getSkipLimitBehaviorMethod.returnEarly("unlimited") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/pandora/misc/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/pandora/misc/Fingerprints.kt index 2a14e0ed1b..21facc0211 100644 --- a/patches/src/main/kotlin/app/revanced/patches/pandora/misc/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/pandora/misc/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.pandora.misc -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val skipLimitBehaviorFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getSkipLimitBehavior" && classDef.endsWith("UserData;") - } -} \ No newline at end of file +internal val BytecodePatchContext.getSkipLimitBehaviorMethod by gettingFirstMethodDeclaratively { + name("getSkipLimitBehavior") + definingClass("UserData;") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/Fingerprints.kt index 35c1956649..3706f882e8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/Fingerprints.kt @@ -1,10 +1,12 @@ package app.revanced.patches.peacocktv.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.accessFlags +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val mediaTailerAdServiceFingerprint = fingerprint { +internal val BytecodePatchContext.mediaTailerAdServiceMethod by gettingFirstMethodDeclaratively("Could not build MT Advertising service") { accessFlags(AccessFlags.PUBLIC) - returns("Ljava/lang/Object") - strings("Could not build MT Advertising service") + returnType("Ljava/lang/Object;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/HideAdsPatch.kt index 52e07e2389..553add2b72 100644 --- a/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/peacocktv/ads/HideAdsPatch.kt @@ -10,7 +10,7 @@ val hideAdsPatch = bytecodePatch( ) { compatibleWith("com.peacocktv.peacockandroid") - execute { - mediaTailerAdServiceFingerprint.method.returnEarly(false) + apply { + mediaTailerAdServiceMethod.returnEarly(false) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt index 90c0bbb919..172398045c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt @@ -1,11 +1,17 @@ package app.revanced.patches.photomath.detection.deviceid +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.Opcode -import app.revanced.patcher.fingerprint -internal val getDeviceIdFingerprint = fingerprint { - returns("Ljava/lang/String;") - parameters() +internal val BytecodePatchContext.getDeviceIdMethod by gettingFirstMethodDeclaratively { + returnType("Ljava/lang/String;") + parameterTypes() opcodes( Opcode.SGET_OBJECT, Opcode.IGET_OBJECT, @@ -18,4 +24,4 @@ internal val getDeviceIdFingerprint = fingerprint { Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_VIRTUAL, ) -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt index b502eb40b3..d1a4ca5cb9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt @@ -6,7 +6,7 @@ import app.revanced.util.returnEarly import kotlin.random.Random @Suppress("unused") -val getDeviceIdPatch = bytecodePatch( +val spoofDeviceIDPatch = bytecodePatch( name = "Spoof device ID", description = "Spoofs device ID to mitigate manual bans by developers.", ) { @@ -14,7 +14,7 @@ val getDeviceIdPatch = bytecodePatch( compatibleWith("com.microblink.photomath") - execute { - getDeviceIdFingerprint.method.returnEarly(Random.nextLong().toString(16)) + apply { + getDeviceIdMethod.returnEarly(Random.nextLong().toString(16)) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt index c6563270e1..69fb11ef55 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt @@ -1,9 +1,13 @@ package app.revanced.patches.photomath.detection.signature +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -import app.revanced.patcher.fingerprint -internal val checkSignatureFingerprint = fingerprint { +internal val BytecodePatchContext.checkSignatureMethodMatch by composingFirstMethod("SHA") { opcodes( Opcode.CONST_STRING, Opcode.INVOKE_STATIC, @@ -14,5 +18,4 @@ internal val checkSignatureFingerprint = fingerprint { Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT, ) - strings("SHA") } 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..a4d81d204e 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 @@ -1,18 +1,18 @@ package app.revanced.patches.photomath.detection.signature -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction val signatureDetectionPatch = bytecodePatch( description = "Disables detection of incorrect signature.", ) { + apply { + val replacementIndex = checkSignatureMethodMatch[-1] - execute { - val replacementIndex = checkSignatureFingerprint.patternMatch!!.endIndex - val checkRegister = - checkSignatureFingerprint.method.getInstruction(replacementIndex).registerA - checkSignatureFingerprint.method.replaceInstruction(replacementIndex, "const/4 v$checkRegister, 0x1") + val checkRegister = checkSignatureMethodMatch.method.getInstruction(replacementIndex) + .registerA + checkSignatureMethodMatch.method.replaceInstruction(replacementIndex, "const/4 v$checkRegister, 0x1") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt index 301f2f9a52..0459aa62dd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt @@ -1,12 +1,15 @@ package app.revanced.patches.photomath.misc.annoyances -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val hideUpdatePopupFingerprint = fingerprint { +internal val BytecodePatchContext.hideUpdatePopupMethod by gettingFirstMethodDeclaratively { + definingClass("Lcom/microblink/photomath/main/activity/MainActivity;") accessFlags(AccessFlags.FINAL, AccessFlags.PUBLIC) - returns("V") + returnType("V") opcodes( Opcode.CONST_HIGH16, Opcode.INVOKE_VIRTUAL, // ViewPropertyAnimator.alpha(1.0f) @@ -14,8 +17,4 @@ internal val hideUpdatePopupFingerprint = fingerprint { Opcode.CONST_WIDE_16, Opcode.INVOKE_VIRTUAL, // ViewPropertyAnimator.setDuration(1000L) ) - custom { method, _ -> - // The popup is shown only in the main activity - method.definingClass == "Lcom/microblink/photomath/main/activity/MainActivity;" - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt index e8e09b4174..b7713695d3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.photomath.misc.annoyances -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch @@ -13,8 +13,8 @@ val hideUpdatePopupPatch = bytecodePatch( compatibleWith("com.microblink.photomath") - execute { - hideUpdatePopupFingerprint.method.addInstructions( + apply { + hideUpdatePopupMethod.addInstructions( 2, // Insert after the null check. "return-void", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt index 6e3da98026..77475d2990 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt @@ -1,19 +1,13 @@ package app.revanced.patches.photomath.misc.unlock.bookpoint -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly val enableBookpointPatch = bytecodePatch( description = "Enables textbook access", ) { - execute { - isBookpointEnabledFingerprint.method.replaceInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + isBookpointEnabledMethod.returnEarly(true) // TODO: CHECK IF THIS IS FINE IN REPLACEMENT OF replaceInstructions } } diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt index 6722f4223e..f5e77bf84e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt @@ -1,16 +1,19 @@ package app.revanced.patches.photomath.misc.unlock.bookpoint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.accessFlags +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val isBookpointEnabledFingerprint = fingerprint { +internal val BytecodePatchContext.isBookpointEnabledMethod by gettingFirstMethodDeclaratively( + "NoGeoData", + "NoCountryInGeo", + "RemoteConfig", + "GeoRCMismatch" +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - strings( - "NoGeoData", - "NoCountryInGeo", - "RemoteConfig", - "GeoRCMismatch" - ) -} \ No newline at end of file + returnType("Z") + parameterTypes() +} 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..c011cdb947 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 @@ -1,13 +1,14 @@ package app.revanced.patches.photomath.misc.unlock.plus +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val isPlusUnlockedFingerprint = fingerprint{ +internal val BytecodePatchContext.isPlusUnlockedMethod by gettingFirstMethodDeclaratively("genius") { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - strings("genius") - custom { _, classDef -> - classDef.endsWith("/User;") - } -} \ No newline at end of file + returnType("Z") + definingClass("/User;") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt index 1339a44775..eaffe6c19d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt @@ -1,25 +1,17 @@ package app.revanced.patches.photomath.misc.unlock.plus -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch import app.revanced.patches.photomath.misc.unlock.bookpoint.enableBookpointPatch +import app.revanced.util.returnEarly @Suppress("unused") -val unlockPlusPatch = bytecodePatch( - name = "Unlock plus", -) { +val unlockPlusPatch = bytecodePatch("Unlock plus") { dependsOn(signatureDetectionPatch, enableBookpointPatch) compatibleWith("com.microblink.photomath") - execute { - isPlusUnlockedFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + isPlusUnlockedMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt index 8c2d579ef5..4a2a0fe2de 100644 --- a/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt @@ -1,11 +1,17 @@ package app.revanced.patches.piccomafr.misc +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val getAndroidIdFingerprint = fingerprint { +internal val BytecodePatchContext.getAndroidIdMethod by gettingFirstMethodDeclaratively( + "context", + "android_id" +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters("Landroid/content/Context;") - strings("context", "android_id") -} \ No newline at end of file + returnType("Ljava/lang/String;") + parameterTypes("Landroid/content/Context;") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt index 458d715aa7..98e303deea 100644 --- a/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt @@ -1,12 +1,11 @@ package app.revanced.patches.piccomafr.misc -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.stringOption import app.revanced.util.returnEarly @Suppress("unused") -val spoofAndroidDeviceIdPatch = bytecodePatch( +val spoofAndroidDeviceIDPatch = bytecodePatch( name = "Spoof Android device ID", description = "Spoofs the Android device ID used by the app for account authentication." + "This can be used to copy the account to another device.", @@ -32,14 +31,13 @@ val spoofAndroidDeviceIdPatch = bytecodePatch( ) val androidDeviceId by stringOption( - key = "android-device-id", + name = "Android device ID", default = "0011223344556677", - title = "Android device ID", description = "The Android device ID to spoof to.", required = true, ) { it!!.matches("[A-Fa-f0-9]{16}".toRegex()) } - execute { - getAndroidIdFingerprint.method.returnEarly(androidDeviceId!!) + apply { + getAndroidIdMethod.returnEarly(androidDeviceId!!) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt index 32e02f37aa..d37280e454 100644 --- a/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.piccomafr.tracking -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.getReference import com.android.tools.smali.dexlib2.Opcode @@ -33,35 +33,31 @@ val disableTrackingPatch = bytecodePatch( ), ) - execute { - facebookSDKFingerprint.method.apply { - instructions.filter { instruction -> - instruction.opcode == Opcode.CONST_STRING - }.forEach { instruction -> - instruction as OneRegisterInstruction + apply { + facebookSDKMethod.instructions.filter { instruction -> + instruction.opcode == Opcode.CONST_STRING + }.forEach { instruction -> + instruction as OneRegisterInstruction - replaceInstruction( - instruction.location.index, - "const-string v${instruction.registerA}, \"example.com\"", - ) - } + facebookSDKMethod.replaceInstruction( + instruction.location.index, + "const-string v${instruction.registerA}, \"example.com\"", + ) } - firebaseInstallFingerprint.method.apply { - instructions.filter { - it.opcode == Opcode.CONST_STRING - }.filter { - it.getReference()?.string == "firebaseinstallations.googleapis.com" - }.forEach { instruction -> - instruction as OneRegisterInstruction + firebaseInstallMethod.instructions.filter { + it.opcode == Opcode.CONST_STRING + }.filter { + it.getReference()?.string == "firebaseinstallations.googleapis.com" + }.forEach { instruction -> + instruction as OneRegisterInstruction - replaceInstruction( - instruction.location.index, - "const-string v${instruction.registerA}, \"example.com\"", - ) - } + firebaseInstallMethod.replaceInstruction( + instruction.location.index, + "const-string v${instruction.registerA}, \"example.com\"", + ) } - appMeasurementFingerprint.method.addInstruction(0, "return-void") + appMeasurementMethod.addInstruction(0, "return-void") } } 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..084c3b2b06 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 @@ -1,24 +1,20 @@ package app.revanced.patches.piccomafr.tracking -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.accessFlags +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val appMeasurementFingerprint = fingerprint { +internal val BytecodePatchContext.appMeasurementMethod by gettingFirstMethodDeclaratively("config/app/", "Fetching remote configuration") { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") - strings("config/app/", "Fetching remote configuration") + returnType("V") } -internal val facebookSDKFingerprint = fingerprint { +internal val BytecodePatchContext.facebookSDKMethod by gettingFirstMethodDeclaratively("instagram.com", "facebook.com") { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - returns("V") - strings("instagram.com", "facebook.com") } -internal val firebaseInstallFingerprint = fingerprint { +internal val BytecodePatchContext.firebaseInstallMethod by gettingFirstMethodDeclaratively("https://%s/%s/%s", "firebaseinstallations.googleapis.com") { accessFlags(AccessFlags.PRIVATE) - strings( - "https://%s/%s/%s", - "firebaseinstallations.googleapis.com", - ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt index 3e2addaa06..d7b0566e3e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt @@ -1,12 +1,16 @@ package app.revanced.patches.pixiv.ads +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val shouldShowAdsFingerprint = fingerprint { +internal val BytecodePatchContext.shouldShowAdsMethod by gettingFirstMethodDeclaratively { + definingClass("AdUtils;") + name("shouldShowAds") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - custom { methodDef, classDef -> - classDef.type.endsWith("AdUtils;") && methodDef.name == "shouldShowAds" - } + returnType("Z") } diff --git a/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt index 4454895b11..269ea8bd97 100644 --- a/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt @@ -4,12 +4,10 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("jp.pxv.android"("6.141.1")) - execute { - shouldShowAdsFingerprint.method.returnEarly(false) + apply { + shouldShowAdsMethod.returnEarly(false) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/Fingerprints.kt index ac3a1c43a5..e929cb1a42 100644 --- a/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/Fingerprints.kt @@ -1,33 +1,33 @@ package app.revanced.patches.primevideo.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val enterServerInsertedAdBreakStateFingerprint = fingerprint { +internal val BytecodePatchContext.enterServerInsertedAdBreakStateMethod by gettingFirstMethodDeclaratively { + name("enter") + definingClass("Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;") accessFlags(AccessFlags.PUBLIC) - parameters("Lcom/amazon/avod/fsm/Trigger;") - returns("V") + parameterTypes("Lcom/amazon/avod/fsm/Trigger;") + returnType("V") opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT, Opcode.CONST_4, - Opcode.CONST_4 + Opcode.CONST_4, ) - custom { method, classDef -> - method.name == "enter" && classDef.type == "Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;" - } } -internal val doTriggerFingerprint = fingerprint { +internal val BytecodePatchContext.doTriggerMethod by gettingFirstMethodDeclaratively { + name("doTrigger") + definingClass("Lcom/amazon/avod/fsm/StateBase;") accessFlags(AccessFlags.PROTECTED) - returns("V") + returnType("V") opcodes( Opcode.IGET_OBJECT, Opcode.INVOKE_INTERFACE, - Opcode.RETURN_VOID + Opcode.RETURN_VOID, ) - custom { method, classDef -> - method.name == "doTrigger" && classDef.type == "Lcom/amazon/avod/fsm/StateBase;" - } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/SkipAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/SkipAdsPatch.kt index c204c729e2..a78fcf1269 100644 --- a/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/SkipAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/primevideo/ads/SkipAdsPatch.kt @@ -1,11 +1,15 @@ package app.revanced.patches.primevideo.ads -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.primevideo.misc.extension.sharedExtensionPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow 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.reference.MethodReference @Suppress("unused") val skipAdsPatch = bytecodePatch( @@ -18,15 +22,19 @@ val skipAdsPatch = bytecodePatch( // Skip all the logic in ServerInsertedAdBreakState.enter(), which plays all the ad clips in this // ad break. Instead, force the video player to seek over the entire break and reset the state machine. - execute { + apply { // Force doTrigger() access to public so we can call it from our extension. - doTriggerFingerprint.method.accessFlags = AccessFlags.PUBLIC.value; + doTriggerMethod.accessFlags = AccessFlags.PUBLIC.value - val getPlayerIndex = enterServerInsertedAdBreakStateFingerprint.patternMatch!!.startIndex - enterServerInsertedAdBreakStateFingerprint.method.apply { + enterServerInsertedAdBreakStateMethod.apply { // Get register that stores VideoPlayer: // invoke-virtual ->getPrimaryPlayer() // move-result-object { playerRegister } + val getPlayerIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.name == "getPrimaryPlayer" + } + val playerRegister = getInstruction(getPlayerIndex + 1).registerA // Reuse the params from the original method: @@ -37,9 +45,8 @@ val skipAdsPatch = bytecodePatch( """ invoke-static { p0, p1, v$playerRegister }, Lapp/revanced/extension/primevideo/ads/SkipAdsPatch;->enterServerInsertedAdBreakState(Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;Lcom/amazon/avod/media/ads/internal/state/AdBreakTrigger;Lcom/amazon/avod/media/playback/VideoPlayer;)V return-void - """ + """, ) } } } - 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/primevideo/misc/permissions/RenamePermissionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/primevideo/misc/permissions/RenamePermissionsPatch.kt index 9a671879f6..20f9e5217e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/primevideo/misc/permissions/RenamePermissionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/primevideo/misc/permissions/RenamePermissionsPatch.kt @@ -7,11 +7,11 @@ import app.revanced.util.getNode import org.w3c.dom.Element @Suppress("unused") -val renamePermissionsPatch = resourcePatch( +val renameSharedPermissionsPatch = resourcePatch( name = "Rename shared permissions", description = "Rename certain permissions shared across Amazon apps. " + - "Applying this patch can fix installation errors, but can also break features in certain apps.", - use = false + "Applying this patch can fix installation errors, but can also break features in certain apps.", + use = false, ) { compatibleWith("com.amazon.avod.thirdpartyclient") @@ -21,10 +21,10 @@ val renamePermissionsPatch = resourcePatch( "com.amazon.dcp.sso.permission.account.changed", "com.amazon.dcp.sso.permission.AmazonAccountPropertyService.property.changed", "com.amazon.identity.permission.CALL_AMAZON_DEVICE_INFORMATION_PROVIDER", - "com.amazon.appmanager.preload.permission.READ_PRELOAD_DEVICE_INFO_PROVIDER" + "com.amazon.appmanager.preload.permission.READ_PRELOAD_DEVICE_INFO_PROVIDER", ) - execute { + apply { document("AndroidManifest.xml").use { document -> val manifest = document.getNode("manifest") as Element diff --git a/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/Fingerprints.kt index bd4431d567..7001aa38d0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/Fingerprints.kt @@ -1,23 +1,22 @@ package app.revanced.patches.primevideo.video.speed -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val playbackUserControlsInitializeFingerprint = fingerprint { +internal val BytecodePatchContext.playbackUserControlsInitializeMethod by gettingFirstMethodDeclaratively { + name("initialize") + definingClass("Lcom/amazon/avod/playbackclient/activity/feature/PlaybackUserControlsFeature;") accessFlags(AccessFlags.PUBLIC) - parameters("Lcom/amazon/avod/playbackclient/PlaybackInitializationContext;") - returns("V") - custom { method, classDef -> - method.name == "initialize" && classDef.type == "Lcom/amazon/avod/playbackclient/activity/feature/PlaybackUserControlsFeature;" - } + parameterTypes("Lcom/amazon/avod/playbackclient/PlaybackInitializationContext;") + returnType("V") } -internal val playbackUserControlsPrepareForPlaybackFingerprint = fingerprint { +internal val BytecodePatchContext.playbackUserControlsPrepareForPlaybackMethod by gettingFirstMethodDeclaratively { + name("prepareForPlayback") + definingClass("Lcom/amazon/avod/playbackclient/activity/feature/PlaybackUserControlsFeature;") accessFlags(AccessFlags.PUBLIC) - parameters("Lcom/amazon/avod/playbackclient/PlaybackContext;") - returns("V") - custom { method, classDef -> - method.name == "prepareForPlayback" && - classDef.type == "Lcom/amazon/avod/playbackclient/activity/feature/PlaybackUserControlsFeature;" - } + parameterTypes("Lcom/amazon/avod/playbackclient/PlaybackContext;") + returnType("V") } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/PlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/PlaybackSpeedPatch.kt index ef318d60cf..cce68b6c13 100644 --- a/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/PlaybackSpeedPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/primevideo/video/speed/PlaybackSpeedPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.primevideo.video.speed -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.primevideo.misc.extension.sharedExtensionPatch import app.revanced.util.getReference @@ -10,9 +10,10 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference -private const val EXTENSION_CLASS_DESCRIPTOR = +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch;" +@Suppress("unused") val playbackSpeedPatch = bytecodePatch( name = "Playback speed", description = "Adds playback speed controls to the video player.", @@ -22,35 +23,35 @@ val playbackSpeedPatch = bytecodePatch( ) compatibleWith( - "com.amazon.avod.thirdpartyclient"("3.0.412.2947") + "com.amazon.avod.thirdpartyclient"("3.0.412.2947"), ) - execute { - playbackUserControlsInitializeFingerprint.method.apply { + apply { + playbackUserControlsInitializeMethod.apply { val getIndex = indexOfFirstInstructionOrThrow { - opcode == Opcode.IPUT_OBJECT && - getReference()?.name == "mUserControls" + opcode == Opcode.IPUT_OBJECT && + getReference()?.name == "mUserControls" } - + val getRegister = getInstruction(getIndex).registerA - + addInstructions( getIndex + 1, """ invoke-static { v$getRegister }, $EXTENSION_CLASS_DESCRIPTOR->initializeSpeedOverlay(Landroid/view/View;)V - """ + """, ) } - playbackUserControlsPrepareForPlaybackFingerprint.method.apply { + playbackUserControlsPrepareForPlaybackMethod.apply { addInstructions( 0, """ invoke-virtual { p1 }, Lcom/amazon/avod/playbackclient/PlaybackContext;->getPlayer()Lcom/amazon/video/sdk/player/Player; move-result-object v0 invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setPlayer(Lcom/amazon/video/sdk/player/Player;)V - """ + """, ) } } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/protonmail/account/RemoveFreeAccountsLimitPatch.kt b/patches/src/main/kotlin/app/revanced/patches/protonmail/account/RemoveFreeAccountsLimitPatch.kt index cd81f1bb41..bc99597b91 100644 --- a/patches/src/main/kotlin/app/revanced/patches/protonmail/account/RemoveFreeAccountsLimitPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/protonmail/account/RemoveFreeAccountsLimitPatch.kt @@ -10,7 +10,7 @@ val removeFreeAccountsLimitPatch = resourcePatch( ) { compatibleWith("ch.protonmail.android"("4.15.0")) - execute { + apply { document("res/values/integers.xml").use { document -> document.documentElement.childNodes.findElementByAttributeValueOrThrow( "name", diff --git a/patches/src/main/kotlin/app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatch.kt index 44098a2df5..9f0a0fd971 100644 --- a/patches/src/main/kotlin/app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatch.kt @@ -12,7 +12,7 @@ val removeSentFromSignaturePatch = resourcePatch( ) { compatibleWith("ch.protonmail.android"("4.15.0")) - execute { + apply { val stringResourceFiles = mutableListOf() get("res").walk().forEach { file -> @@ -26,7 +26,7 @@ val removeSentFromSignaturePatch = resourcePatch( document(filePath.absolutePath).use { document -> var node = document.documentElement.childNodes.findElementByAttributeValue( "name", - "mail_settings_identity_mobile_footer_default_free" + "mail_settings_identity_mobile_footer_default_free", ) // String is not localized in all languages. diff --git a/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/Fingerprints.kt index bc8ad39250..0791f3a88f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/Fingerprints.kt @@ -1,16 +1,13 @@ package app.revanced.patches.protonvpn.delay -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext - -internal val longDelayFingerprint = fingerprint { - custom { method, _ -> - method.name == "getChangeServerLongDelayInSeconds" - } +internal val BytecodePatchContext.longDelayMethod by gettingFirstMethodDeclaratively { + name("getChangeServerLongDelayInSeconds") } -internal val shortDelayFingerprint = fingerprint { - custom { method, _ -> - method.name == "getChangeServerShortDelayInSeconds" - } -} \ No newline at end of file +internal val BytecodePatchContext.shortDelayMethod by gettingFirstMethodDeclaratively { + name("getChangeServerShortDelayInSeconds") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/RemoveDelayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/RemoveDelayPatch.kt index 512813e596..9783168dbc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/RemoveDelayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/protonvpn/delay/RemoveDelayPatch.kt @@ -10,8 +10,8 @@ val removeDelayPatch = bytecodePatch( ) { compatibleWith("ch.protonvpn.android") - execute { - longDelayFingerprint.method.returnEarly(0) - shortDelayFingerprint.method.returnEarly(0) + apply { + longDelayMethod.returnEarly(0) + shortDelayMethod.returnEarly(0) } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/Fingerprints.kt index 639141723d..09cbb977bc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/Fingerprints.kt @@ -1,20 +1,17 @@ package app.revanced.patches.protonvpn.splittunneling -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val enableSplitTunnelingUiFingerprint = fingerprint { - strings("currentModeAppNames") +internal val BytecodePatchContext.enableSplitTunnelingUiMethodMatch by composingFirstMethod("currentModeAppNames") { opcodes( Opcode.MOVE_OBJECT, Opcode.MOVE_FROM16, - Opcode.INVOKE_DIRECT_RANGE + Opcode.INVOKE_DIRECT_RANGE, ) } -internal val initializeSplitTunnelingSettingsUIFingerprint = fingerprint { - custom { method, _ -> - method.name == "applyRestrictions" - } -} \ No newline at end of file +internal val BytecodePatchContext.initializeSplitTunnelingSettingsUIMethod by gettingFirstMethodDeclaratively { + name("applyRestrictions") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/UnlockSplitTunneling.kt b/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/UnlockSplitTunneling.kt index f50a5f9934..79bc78ec1a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/UnlockSplitTunneling.kt +++ b/patches/src/main/kotlin/app/revanced/patches/protonvpn/splittunneling/UnlockSplitTunneling.kt @@ -1,8 +1,8 @@ package app.revanced.patches.protonvpn.splittunneling -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.removeInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow @@ -10,25 +10,21 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Suppress("unused") -val unlockSplitTunnelingPatch = - bytecodePatch( - name = "Unlock split tunneling", - ) { - compatibleWith("ch.protonvpn.android") +val unlockSplitTunnelingPatch = bytecodePatch("Unlock split tunneling") { + compatibleWith("ch.protonvpn.android") - execute { - val registerIndex = enableSplitTunnelingUiFingerprint.patternMatch!!.endIndex - 1 + apply { + enableSplitTunnelingUiMethodMatch.let { + val registerIndex = it[-1] - 1 + val register = it.method.getInstruction(registerIndex).registerA + it.method.replaceInstruction(registerIndex, "const/4 v$register, 0x0") + } - enableSplitTunnelingUiFingerprint.method.apply { - val register = getInstruction(registerIndex).registerA - replaceInstruction(registerIndex, "const/4 v$register, 0x0") - } - - initializeSplitTunnelingSettingsUIFingerprint.method.apply { - val initSettingsIndex = indexOfFirstInstructionOrThrow { - getReference()?.name == "getSplitTunneling" - } - removeInstruction(initSettingsIndex - 1) + initializeSplitTunnelingSettingsUIMethod.apply { + val initSettingsIndex = indexOfFirstInstructionOrThrow { + getReference()?.name == "getSplitTunneling" } + removeInstruction(initSettingsIndex - 1) } } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt index a4d2c9e221..0d1c0e1d8a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt @@ -1,12 +1,16 @@ package app.revanced.patches.rar.misc.annoyances.purchasereminder -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val showReminderFingerprint = fingerprint { +internal val BytecodePatchContext.showReminderMethod by gettingFirstMethodDeclaratively { + definingClass("AdsNotify;") + name("show") accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("V") - custom { method, _ -> - method.definingClass.endsWith("AdsNotify;") && method.name == "show" - } + returnType("V") } diff --git a/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt index 885c2400f4..d755c697d8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt @@ -1,17 +1,16 @@ package app.revanced.patches.rar.misc.annoyances.purchasereminder -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") val hidePurchaseReminderPatch = bytecodePatch( name = "Hide purchase reminder", description = "Hides the popup that reminds you to purchase the app.", - ) { compatibleWith("com.rarlab.rar") - execute { - showReminderFingerprint.method.addInstruction(0, "return-void") + apply { + showReminderMethod.addInstruction(0, "return-void") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt deleted file mode 100644 index 62e8d5d84d..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt +++ /dev/null @@ -1,34 +0,0 @@ -package app.revanced.patches.reddit.ad.banner - -import app.revanced.patcher.patch.resourcePatch - -// Note that for now, this patch and anything using it will only work on -// Reddit 2024.17.0 or older. Newer versions will crash during patching. -// See https://github.com/ReVanced/revanced-patches/issues/3099 -val hideBannerPatch = resourcePatch( - description = "Hides banner ads from comments on subreddits.", -) { - execute { - val resourceFilePath = "res/layout/merge_listheader_link_detail.xml" - - document(resourceFilePath).use { document -> - document.getElementsByTagName("merge").item(0).childNodes.apply { - val attributes = arrayOf("height", "width") - - for (i in 1 until length) { - val view = item(i) - if ( - view.hasAttributes() && - view.attributes.getNamedItem("android:id").nodeValue.endsWith("ad_view_stub") - ) { - attributes.forEach { attribute -> - view.attributes.getNamedItem("android:layout_$attribute").nodeValue = "0.0dip" - } - - break - } - } - } - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt index 4fa8dbc81a..c43dbf594b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt @@ -1,10 +1,11 @@ package app.revanced.patches.reddit.ad.comments -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val hideCommentAdsFingerprint = fingerprint { - custom { method, classDef -> - method.name == "invokeSuspend" && - classDef.contains("LoadAdsCombinedCall") - } +internal val BytecodePatchContext.hideCommentAdsMethod by gettingFirstMethodDeclaratively { + name("invokeSuspend") + definingClass("LoadAdsCombinedCall") } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt index 34f5b97813..4baebd53be 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt @@ -1,13 +1,13 @@ package app.revanced.patches.reddit.ad.comments -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.extensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch val hideCommentAdsPatch = bytecodePatch( - description = "Removes ads in the comments.", + description = "Removes ads in the comments." ) { - execute { - hideCommentAdsFingerprint.method.replaceInstructions(0, "return-object p1") + apply { + hideCommentAdsMethod.replaceInstructions(0, "return-object p1") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt index 22cf466fce..54208c3825 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt @@ -1,16 +1,21 @@ package app.revanced.patches.reddit.ad.general -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.Opcode -internal val adPostFingerprint = fingerprint { - returns("V") - // "children" are present throughout multiple versions - strings("children") - custom { _, classDef -> classDef.endsWith("Listing;") } +internal val BytecodePatchContext.adPostMethod by gettingFirstMethodDeclaratively("children") { + definingClass("Listing;") + returnType("V") } -internal val newAdPostFingerprint = fingerprint { - opcodes(Opcode.INVOKE_VIRTUAL) - strings("feedElement", "com.reddit.cookie") +internal val BytecodePatchContext.newAdPostMethod by gettingFirstMethodDeclaratively( + "feedElement", + "com.reddit.cookie", +) { + instructions(Opcode.INVOKE_VIRTUAL()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt index c25277bbdd..8cf4a3aa67 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt @@ -1,10 +1,12 @@ package app.revanced.patches.reddit.ad.general -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.reddit.ad.comments.hideCommentAdsPatch import app.revanced.patches.reddit.misc.extension.sharedExtensionPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c @@ -12,44 +14,40 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { dependsOn(hideCommentAdsPatch, sharedExtensionPatch) compatibleWith("com.reddit.frontpage") - execute { + apply { // region Filter promoted ads (does not work in popular or latest feed) val filterMethodDescriptor = "Lapp/revanced/extension/reddit/patches/FilterPromotedLinksPatch;" + "->filterChildren(Ljava/lang/Iterable;)Ljava/util/List;" - adPostFingerprint.method.apply { - val setPostsListChildren = implementation!!.instructions.first { instruction -> - if (instruction.opcode != Opcode.IPUT_OBJECT) return@first false + val setPostsListChildren = adPostMethod.implementation!!.instructions.first { instruction -> + if (instruction.opcode != Opcode.IPUT_OBJECT) return@first false - val reference = (instruction as ReferenceInstruction).reference as FieldReference - reference.name == "children" - } - - val castedInstruction = setPostsListChildren as Instruction22c - val itemsRegister = castedInstruction.registerA - val listInstanceRegister = castedInstruction.registerB - - // postsList.children = filterChildren(postListItems) - removeInstruction(setPostsListChildren.location.index) - addInstructions( - setPostsListChildren.location.index, - """ - invoke-static {v$itemsRegister}, $filterMethodDescriptor - move-result-object v0 - iput-object v0, v$listInstanceRegister, ${castedInstruction.reference} - """, - ) + val reference = (instruction as ReferenceInstruction).reference as FieldReference + reference.name == "children" } + val castedInstruction = setPostsListChildren as Instruction22c + val itemsRegister = castedInstruction.registerA + val listInstanceRegister = castedInstruction.registerB + + // postsList.children = filterChildren(postListItems) + adPostMethod.removeInstruction(setPostsListChildren.location.index) + adPostMethod.addInstructions( + setPostsListChildren.location.index, + """ + invoke-static {v$itemsRegister}, $filterMethodDescriptor + move-result-object v0 + iput-object v0, v$listInstanceRegister, ${castedInstruction.reference} + """, + ) + // endregion // region Remove ads from popular and latest feed @@ -58,15 +56,13 @@ val hideAdsPatch = bytecodePatch( // AdElementConverter is conveniently responsible for inserting all feed ads. // By removing the appending instruction no ad posts gets appended to the feed. - val index = newAdPostFingerprint.originalMethod.implementation!!.instructions.indexOfFirst { - if (it.opcode != Opcode.INVOKE_VIRTUAL) return@indexOfFirst false - - val reference = (it as ReferenceInstruction).reference as MethodReference + val index = newAdPostMethod.indexOfFirstInstruction { + val reference = getReference() ?: return@indexOfFirstInstruction false reference.name == "add" && reference.definingClass == "Ljava/util/ArrayList;" } - newAdPostFingerprint.method.removeInstruction(index) + newAdPostMethod.removeInstruction(index) } // endregion diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixRedgifsApiPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixRedgifsApiPatch.kt index 92ff76f9e7..fabb9b9b00 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixRedgifsApiPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixRedgifsApiPatch.kt @@ -8,9 +8,9 @@ const val INSTALL_NEW_CLIENT_METHOD = "install(Lokhttp3/OkHttpClient${'$'}Builde const val CREATE_NEW_CLIENT_METHOD = "createClient()Lokhttp3/OkHttpClient;" fun fixRedgifsApiPatch( - extensionPatch: Patch<*>, + extensionPatch: Patch, block: BytecodePatchBuilder.() -> Unit = {}, -) = bytecodePatch(name = "Fix Redgifs API") { +) = bytecodePatch("Fix Redgifs API") { dependsOn(extensionPatch) block() diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt index ccc0346a66..106e1ba23c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt @@ -8,9 +8,9 @@ const val RESOLVE_S_LINK_METHOD = "patchResolveSLink(Ljava/lang/String;)Z" const val SET_ACCESS_TOKEN_METHOD = "patchSetAccessToken(Ljava/lang/String;)V" fun fixSLinksPatch( - extensionPatch: Patch<*>, + extensionPatch: Patch, block: BytecodePatchBuilder.() -> Unit = {}, -) = bytecodePatch(name = "Fix /s/ links") { +) = bytecodePatch("Fix /s/ links") { dependsOn(extensionPatch) block() diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt index b21b36a0d7..a4148df753 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt @@ -20,15 +20,13 @@ fun spoofClientPatch( ) { block( stringOption( - "client-id", - null, - null, - "OAuth client ID", - "The Reddit OAuth client ID. " + + name = "OAuth client ID", + values = null, + description = "The Reddit OAuth client ID. " + "You can get your client ID from https://www.reddit.com/prefs/apps. " + "The application type has to be \"Installed app\" " + "and the redirect URI has to be set to \"$redirectUri\".", - true, + required = true, ), ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt index bb87c21148..07fed42809 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt @@ -1,19 +1,17 @@ package app.revanced.patches.reddit.customclients.baconreader.api -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext -internal val getAuthorizationUrlFingerprint = fingerprint { - strings("client_id=zACVn0dSFGdWqQ") -} -internal val getClientIdFingerprint = fingerprint { - strings("client_id=zACVn0dSFGdWqQ") - custom { method, classDef -> - if (!classDef.endsWith("RedditOAuth;")) return@custom false - - method.name == "getAuthorizeUrl" - } +internal val BytecodePatchContext.getAuthorizationUrlMethodMatch by composingFirstMethod { + instructions("client_id=zACVn0dSFGdWqQ"()) } -internal val requestTokenFingerprint = fingerprint { - strings("zACVn0dSFGdWqQ", "kDm2tYpu9DqyWFFyPlNcXGEni4k") // App ID and secret. +internal val BytecodePatchContext.requestTokenMethodMatch by composingFirstMethod { + instructions( + "zACVn0dSFGdWqQ"(), + "kDm2tYpu9DqyWFFyPlNcXGEni4k"(String::contains), + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt index df811d347d..dde1e50726 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.reddit.customclients.baconreader.api -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.CompositeMatch +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patches.reddit.customclients.spoofClientPatch import app.revanced.patches.shared.misc.string.replaceStringPatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -11,7 +11,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://baconreader.com/au dependsOn( // Redirects from SSL to WWW domain are bugged causing auth problems. // Manually rewrite the URLs to fix this. - replaceStringPatch("ssl.reddit.com", "www.reddit.com") + replaceStringPatch("ssl.reddit.com", "www.reddit.com"), ) compatibleWith( @@ -21,23 +21,21 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://baconreader.com/au val clientId by clientIdOption - execute { - fun Fingerprint.patch(replacementString: String) { - val clientIdIndex = stringMatches!!.first().index + apply { + fun CompositeMatch.patch(replacementString: String) { + val clientIdIndex = get(0) - method.apply { - val clientIdRegister = getInstruction(clientIdIndex).registerA - replaceInstruction( - clientIdIndex, - "const-string v$clientIdRegister, \"$replacementString\"", - ) - } + val clientIdRegister = method.getInstruction(clientIdIndex).registerA + method.replaceInstruction( + clientIdIndex, + "const-string v$clientIdRegister, \"$replacementString\"", + ) } // Patch client id in authorization url. - getAuthorizationUrlFingerprint.patch("client_id=$clientId") + getAuthorizationUrlMethodMatch.patch("client_id=$clientId") // Patch client id for access token request. - requestTokenFingerprint.patch(clientId!!) + requestTokenMethodMatch.patch(clientId!!) } } 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..e69b9cee80 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,13 +1,15 @@ package app.revanced.patches.reddit.customclients.baconreader.fix.redgifs -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.definingClass +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType - -internal val getOkHttpClientFingerprint = fingerprint { - returns("Lokhttp3/OkHttpClient;") - parameters() - custom { method, classDef -> - classDef.type == "Lcom/onelouder/baconreader/media/gfycat/RedGifsManager;" && method.name == "getOkhttpClient" - } +internal val BytecodePatchContext.getOkHttpClientMethod by gettingFirstMethodDeclaratively { + definingClass("Lcom/onelouder/baconreader/media/gfycat/RedGifsManager;") + name("getOkhttpClient") + returnType("Lokhttp3/OkHttpClient;") + parameterTypes() } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/FixRedgifsApiPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/FixRedgifsApiPatch.kt index 4cd9db006b..3d3e6e3ed6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/FixRedgifsApiPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/FixRedgifsApiPatch.kt @@ -1,9 +1,9 @@ package app.revanced.patches.reddit.customclients.baconreader.fix.redgifs -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.removeInstructions +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.extensions.typeReference import app.revanced.patches.reddit.customclients.INSTALL_NEW_CLIENT_METHOD import app.revanced.patches.reddit.customclients.baconreader.misc.extension.sharedExtensionPatch import app.revanced.patches.reddit.customclients.fixRedgifsApiPatch @@ -12,7 +12,6 @@ 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.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.TypeReference internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/baconreader/FixRedgifsApiPatch;" @@ -25,29 +24,29 @@ val fixRedgifsApi = fixRedgifsApiPatch( "com.onelouder.baconreader.premium", ) - execute { + apply { // region Patch Redgifs OkHttp3 client. - getOkHttpClientFingerprint.method.apply { - // Remove conflicting OkHttp interceptors. - val originalInterceptorInstallIndex = indexOfFirstInstructionOrThrow { - opcode == Opcode.NEW_INSTANCE && getReference()?.type == "Lcom/onelouder/baconreader/media/gfycat/RedGifsManager\$HeaderInterceptor;" - } - removeInstructions(originalInterceptorInstallIndex, 5) - - val index = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.name == "build" && reference.definingClass == "Lokhttp3/OkHttpClient\$Builder;" - } - val register = getInstruction(index).registerC - replaceInstruction( - index, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$INSTALL_NEW_CLIENT_METHOD - """ - ) + // Remove conflicting OkHttp interceptors. + val originalInterceptorInstallIndex = getOkHttpClientMethod.indexOfFirstInstructionOrThrow { + opcode == Opcode.NEW_INSTANCE && typeReference?.type == $$"Lcom/onelouder/baconreader/media/gfycat/RedGifsManager$HeaderInterceptor;" } + getOkHttpClientMethod.removeInstructions(originalInterceptorInstallIndex, 5) - // endregion + val index = getOkHttpClientMethod.indexOfFirstInstructionOrThrow { + val reference = getReference() + reference?.name == "build" && reference.definingClass == $$"Lokhttp3/OkHttpClient$Builder;" + } + val register = getOkHttpClientMethod.getInstruction(index).registerC + + getOkHttpClientMethod.replaceInstruction( + index, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$INSTALL_NEW_CLIENT_METHOD + """ + ) } + + // endregion + } 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..0256df0a77 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,5 @@ 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/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt index fc5cabd218..b28cbbfd24 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt @@ -1,17 +1,14 @@ package app.revanced.patches.reddit.customclients.boostforreddit.ads -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val disableAdsPatch = bytecodePatch( - name = "Disable ads", -) { +val disableAdsPatch = bytecodePatch("Disable ads") { compatibleWith("com.rubenmayayo.reddit") - execute { - arrayOf(maxMediationFingerprint, admobMediationFingerprint).forEach { fingerprint -> - fingerprint.method.addInstructions(0, "return-void") - } + apply { + maxMediationMethod.returnEarly() + admobMediationMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt index 618e2f1456..2c3abba5dd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt @@ -1,11 +1,12 @@ package app.revanced.patches.reddit.customclients.boostforreddit.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext -internal val maxMediationFingerprint = fingerprint { - strings("MaxMediation: Attempting to initialize SDK") -} +internal val BytecodePatchContext.maxMediationMethod by gettingFirstMethodDeclaratively( + "MaxMediation: Attempting to initialize SDK" +) -internal val admobMediationFingerprint = fingerprint { - strings("AdmobMediation: Attempting to initialize SDK") -} +internal val BytecodePatchContext.admobMediationMethod by gettingFirstMethodDeclaratively( + "AdmobMediation: Attempting to initialize SDK" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt index cc06fd3968..6579aa81fa 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt @@ -1,15 +1,15 @@ package app.revanced.patches.reddit.customclients.boostforreddit.api -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val buildUserAgentFingerprint = fingerprint { - strings("%s:%s:%s (by /u/%s)") -} - -internal val getClientIdFingerprint = fingerprint { - custom { method, classDef -> - if (!classDef.endsWith("Credentials;")) return@custom false - - method.name == "getClientId" - } +internal val BytecodePatchContext.buildUserAgentMethod by gettingFirstMethodDeclaratively( + "%s:%s:%s (by /u/%s)", +) + +internal val BytecodePatchContext.getClientIdMethod by gettingFirstMethodDeclaratively { + name("getClientId") + definingClass("Credentials;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt index cfa4031c6b..b99df9d8e2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt @@ -1,20 +1,25 @@ package app.revanced.patches.reddit.customclients.boostforreddit.api -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patches.reddit.customclients.spoofClientPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.returnEarly +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.StringReference +@Suppress("unused") val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption -> compatibleWith("com.rubenmayayo.reddit") val clientId by clientIdOption - execute { + apply { // region Patch client id. - getClientIdFingerprint.method.returnEarly(clientId!!) + getClientIdMethod.returnEarly(clientId!!) // endregion @@ -23,12 +28,13 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") // Use a random user agent. val randomName = (0..100000).random() val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)" - buildUserAgentFingerprint.let { - val userAgentTemplateIndex = it.stringMatches!!.first().index - val register = it.method.getInstruction(userAgentTemplateIndex).registerA - - it.method.replaceInstruction(userAgentTemplateIndex, "const-string v$register, \"$userAgent\"") + + val userAgentTemplateIndex = buildUserAgentMethod.indexOfFirstInstructionOrThrow { + opcode == Opcode.CONST_STRING && getReference()?.string == "%s:%s:%s (by /u/%s)" } + val register = buildUserAgentMethod.getInstruction(userAgentTemplateIndex).registerA + + buildUserAgentMethod.replaceInstruction(userAgentTemplateIndex, "const-string v$register, \"$userAgent\"") // endregion } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt index a2b1530b8b..b8cdd1d636 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt @@ -1,7 +1,13 @@ package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext -internal val downloadAudioFingerprint = fingerprint { - strings("/DASH_audio.mp4", "/audio") +internal val BytecodePatchContext.downloadAudioMethodMatch by composingFirstMethod { + instructions( + "/DASH_audio.mp4"(), + "/audio"(), + ) } 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 deleted file mode 100644 index 40c23a76c1..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt +++ /dev/null @@ -1,30 +0,0 @@ -package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads - -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 - -@Suppress("unused") -val fixAudioMissingInDownloadsPatch = bytecodePatch( - name = "Fix missing audio in video downloads", - description = "Fixes audio missing in videos downloaded from v.redd.it.", -) { - compatibleWith("com.rubenmayayo.reddit") - - execute { - val endpointReplacements = mapOf( - "/DASH_audio.mp4" to "/CMAF_AUDIO_128.mp4", - "/audio" to "/CMAF_AUDIO_64.mp4", - ) - - downloadAudioFingerprint.method.apply { - downloadAudioFingerprint.stringMatches!!.forEach { match -> - val replacement = endpointReplacements[match.string] - val register = getInstruction(match.index).registerA - - replaceInstruction(match.index, "const-string v$register, \"$replacement\"") - } - } - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInVideoDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInVideoDownloadsPatch.kt new file mode 100644 index 0000000000..0eaf76d012 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInVideoDownloadsPatch.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads + +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val fixMissingAudioInVideoDownloadsPatch = bytecodePatch( + name = "Fix missing audio in video downloads", + description = "Fixes audio missing in videos downloaded from v.redd.it.", +) { + compatibleWith("com.rubenmayayo.reddit") + + apply { + val endpointReplacements = arrayOf( + "/DASH_AUDIO_128.mp4", + "/DASH_AUDIO_64.mp4", + ) + + downloadAudioMethodMatch.indices[0].forEachIndexed { i, index -> + val replacement = endpointReplacements[i] + val register = downloadAudioMethodMatch.method.getInstruction(index).registerA + + downloadAudioMethodMatch.method.replaceInstruction(index, "const-string v$register, \"$replacement\"") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/Fingerprints.kt index 1f3560d737..518930b35d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/Fingerprints.kt @@ -1,10 +1,12 @@ package app.revanced.patches.reddit.customclients.boostforreddit.fix.redgifs -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val createOkHttpClientFingerprint = fingerprint { +internal val BytecodePatchContext.createOkHttpClientMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE) opcodes( Opcode.NEW_INSTANCE, @@ -14,7 +16,7 @@ internal val createOkHttpClientFingerprint = fingerprint { Opcode.NEW_INSTANCE, Opcode.INVOKE_DIRECT, Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT + Opcode.MOVE_RESULT_OBJECT, ) - custom { _, classDef -> classDef.sourceFile == "RedGifsAPIv2.java" } + custom { immutableClassDef.sourceFile == "RedGifsAPIv2.java" } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/FixRedgifsApiPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/FixRedgifsApiPatch.kt index 340f4096ca..ec5b904c8d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/FixRedgifsApiPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/FixRedgifsApiPatch.kt @@ -1,12 +1,11 @@ package app.revanced.patches.reddit.customclients.boostforreddit.fix.redgifs -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patches.reddit.customclients.CREATE_NEW_CLIENT_METHOD -import app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.sharedExtensionPatch import app.revanced.patches.reddit.customclients.fixRedgifsApiPatch -import app.revanced.util.getReference +import app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.sharedExtensionPatch import app.revanced.util.indexOfFirstInstructionOrThrow -import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/boostforreddit/FixRedgifsApiPatch;" @@ -16,19 +15,17 @@ val fixRedgifsApi = fixRedgifsApiPatch( ) { compatibleWith("com.rubenmayayo.reddit") - execute { + apply { // region Patch Redgifs OkHttp3 client. - createOkHttpClientFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.name == "build" && reference.definingClass == "Lokhttp3/OkHttpClient\$Builder;" - } - replaceInstruction( - index, - "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->$CREATE_NEW_CLIENT_METHOD" - ) + val index = createOkHttpClientMethod.indexOfFirstInstructionOrThrow { + val reference = methodReference + reference?.name == "build" && reference.definingClass == "Lokhttp3/OkHttpClient\$Builder;" } + createOkHttpClientMethod.replaceInstruction( + index, + "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->$CREATE_NEW_CLIENT_METHOD" + ) // endregion } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt index 665dba5a40..562cb121ce 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt @@ -1,21 +1,22 @@ package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val getOAuthAccessTokenFingerprint = fingerprint { +internal val BytecodePatchContext.getOAuthAccessTokenMethod by gettingFirstMethodDeclaratively("access_token") { + definingClass("Lnet/dean/jraw/http/oauth/OAuthData;") accessFlags(AccessFlags.PUBLIC) - returns("Ljava/lang/String") - strings("access_token") - custom { method, _ -> method.definingClass == "Lnet/dean/jraw/http/oauth/OAuthData;" } + returnType("Ljava/lang/String;") } -internal val handleNavigationFingerprint = fingerprint { - strings( - "android.intent.action.SEARCH", - "subscription", - "sort", - "period", - "boostforreddit.com/themes", - ) -} +internal val BytecodePatchContext.handleNavigationMethod by gettingFirstMethodDeclaratively( + "android.intent.action.SEARCH", + "subscription", + "sort", + "period", + "boostforreddit.com/themes" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt index 076221e473..1100a7389b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt @@ -1,9 +1,9 @@ package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patches.reddit.customclients.RESOLVE_S_LINK_METHOD import app.revanced.patches.reddit.customclients.SET_ACCESS_TOKEN_METHOD import app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.sharedExtensionPatch @@ -17,30 +17,29 @@ val fixSlinksPatch = fixSLinksPatch( ) { compatibleWith("com.rubenmayayo.reddit") - execute { + apply { // region Patch navigation handler. - handleNavigationFingerprint.method.apply { - val urlRegister = "p1" - val tempRegister = "v1" + val urlRegister = "p1" + val tempRegister = "v1" + + handleNavigationMethod.addInstructionsWithLabels( + 0, + """ + invoke-static { $urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->$RESOLVE_S_LINK_METHOD + move-result $tempRegister + if-eqz $tempRegister, :continue + return $tempRegister + """, + ExternalLabel("continue", handleNavigationMethod.getInstruction(0)), + ) - addInstructionsWithLabels( - 0, - """ - invoke-static { $urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->$RESOLVE_S_LINK_METHOD - move-result $tempRegister - if-eqz $tempRegister, :continue - return $tempRegister - """, - ExternalLabel("continue", getInstruction(0)), - ) - } // endregion // region Patch set access token. - getOAuthAccessTokenFingerprint.method.addInstruction( + getOAuthAccessTokenMethod.addInstruction( 3, "invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->$SET_ACCESS_TOKEN_METHOD", ) 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/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt index 4bce1362c2..b8d8da7bef 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt @@ -1,7 +1,6 @@ package app.revanced.patches.reddit.customclients.infinityforreddit.api -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.patch.BytecodePatchContext -internal val apiUtilsFingerprint = fingerprint { - strings("native-lib") -} \ No newline at end of file +internal val BytecodePatchContext.apiUtilsMethod by gettingFirstMethod("native-lib") \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt index b195b49fb2..7b8b2a6d9f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt @@ -1,7 +1,8 @@ package app.revanced.patches.reddit.customclients.infinityforreddit.api -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable -import app.revanced.patcher.util.smali.toInstructions +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.toInstructions import app.revanced.patches.reddit.customclients.spoofClientPatch import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.immutable.ImmutableMethod @@ -11,13 +12,13 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "infinity://localhost") { compatibleWith( "ml.docilealligator.infinityforreddit", "ml.docilealligator.infinityforreddit.plus", - "ml.docilealligator.infinityforreddit.patreon" + "ml.docilealligator.infinityforreddit.patreon", ) val clientId by clientIdOption - execute { - apiUtilsFingerprint.classDef.methods.apply { + apply { + apiUtilsMethod.classDef.methods.apply { val getClientIdMethod = single { it.name == "getId" }.also(::remove) val newGetClientIdMethod = ImmutableMethod( 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..0313a99321 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,13 @@ package app.revanced.patches.reddit.customclients.infinityforreddit.subscription -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.invoke +import app.revanced.patcher.instructions +import app.revanced.patcher.patch.BytecodePatchContext -internal val billingClientOnServiceConnectedFingerprint = fingerprint { - strings("Billing service connected") -} +internal val BytecodePatchContext.billingClientOnServiceConnectedMethod by gettingFirstMethod("Billing service connected") -internal val startSubscriptionActivityFingerprint = fingerprint { - literal { - // Intent start flag only used in the subscription activity - 0x10008000 - } +internal val BytecodePatchContext.startSubscriptionActivityMethod by gettingFirstMethodDeclaratively { + instructions(0x10008000L()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt index ba39c29a52..4cc87895ec 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt @@ -14,13 +14,11 @@ val unlockSubscriptionPatch = bytecodePatch( compatibleWith( "ml.docilealligator.infinityforreddit", "ml.docilealligator.infinityforreddit.plus", - "ml.docilealligator.infinityforreddit.patreon" + "ml.docilealligator.infinityforreddit.patreon", ) - execute { - setOf( - startSubscriptionActivityFingerprint, - billingClientOnServiceConnectedFingerprint, - ).forEach { it.method.returnEarly() } + apply { + billingClientOnServiceConnectedMethod.returnEarly() + startSubscriptionActivityMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt index 0c75087581..3f8b5b49cb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt @@ -1,24 +1,16 @@ package app.revanced.patches.reddit.customclients.joeyforreddit.ads -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy.disablePiracyDetectionPatch +import app.revanced.util.returnEarly @Suppress("unused") -val disableAdsPatch = bytecodePatch( - name = "Disable ads", -) { +val disableAdsPatch = bytecodePatch("Disable ads") { dependsOn(disablePiracyDetectionPatch) compatibleWith("o.o.joey") - execute { - isAdFreeUserFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + isAdFreeUserMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt index 465faf1200..b4924a228c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt @@ -1,10 +1,12 @@ package app.revanced.patches.reddit.customclients.joeyforreddit.ads +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.accessFlags +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val isAdFreeUserFingerprint = fingerprint { +internal val BytecodePatchContext.isAdFreeUserMethod by gettingFirstMethodDeclaratively("AD_FREE_USER") { accessFlags(AccessFlags.PUBLIC) - returns("Z") - strings("AD_FREE_USER") -} \ No newline at end of file + returnType("Z") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt index e6c591748c..37b39069ec 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt @@ -1,28 +1,26 @@ package app.revanced.patches.reddit.customclients.joeyforreddit.api -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val authUtilityUserAgentFingerprint = fingerprint { +internal val BytecodePatchContext.authUtilityUserAgentMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Ljava/lang/String;") + returnType("Ljava/lang/String;") opcodes(Opcode.APUT_OBJECT) - custom { method, classDef -> - classDef.sourceFile == "AuthUtility.java" - } + custom { immutableClassDef.sourceFile == "AuthUtility.java" } } -internal val getClientIdFingerprint = fingerprint { +internal val BytecodePatchContext.getClientIdMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("L") + returnType("L") opcodes( - Opcode.CONST, // R.string.valuable_cid - Opcode.INVOKE_STATIC, // StringMaster.decrypt + Opcode.CONST, + Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT_OBJECT, - Opcode.RETURN_OBJECT + Opcode.RETURN_OBJECT, ) - custom { _, classDef -> - classDef.sourceFile == "AuthUtility.java" - } -} \ No newline at end of file + custom { immutableClassDef.sourceFile == "AuthUtility.java" } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt index e1855a50f5..8e9fa3d23e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.reddit.customclients.joeyforreddit.api -import app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy.disablePiracyDetectionPatch import app.revanced.patches.reddit.customclients.spoofClientPatch +import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch import app.revanced.util.returnEarly val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/authorize_callback") { clientIdOption -> @@ -15,10 +15,10 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/a val clientId by clientIdOption - execute { + apply { // region Patch client id. - getClientIdFingerprint.method.returnEarly(clientId!!) + getClientIdMethod.returnEarly(clientId!!) // endregion @@ -28,7 +28,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/a val randomName = (0..100000).random() val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)" - authUtilityUserAgentFingerprint.method.returnEarly(userAgent) + authUtilityUserAgentMethod.returnEarly(userAgent) // endregion } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt index a6871dbc0e..d037f18959 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt @@ -1,11 +1,11 @@ package app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly +@Suppress("unused") val disablePiracyDetectionPatch = bytecodePatch { - - execute { - piracyDetectionFingerprint.method.addInstruction(0, "return-void") + apply { + detectPiracyMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt index 76343a5309..a1f2311635 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt @@ -1,21 +1,26 @@ package app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val piracyDetectionFingerprint = fingerprint { +internal val BytecodePatchContext.detectPiracyMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("V") + returnType("V") opcodes( Opcode.NEW_INSTANCE, Opcode.CONST_16, Opcode.CONST_WIDE_16, Opcode.INVOKE_DIRECT, Opcode.INVOKE_VIRTUAL, - Opcode.RETURN_VOID + Opcode.RETURN_VOID, ) - custom { _, classDef -> - classDef.endsWith("ProcessLifeCyleListener;") - } -} \ No newline at end of file + definingClass("ProcessLifeCyleListener;") +} 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..cab6b3e7ab 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,25 +1,29 @@ package app.revanced.patches.reddit.customclients.redditisfun.api -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal fun baseClientIdFingerprint(string: String) = fingerprint { - strings("yyOCBp.RHJhDKd", string) +internal val BytecodePatchContext.basicAuthorizationMethodMatch by composingFirstMethod { + instructions( + "yyOCBp.RHJhDKd"(), + "fJOxVwBUyo*=f: compatibleWith( "com.andrewshu.android.reddit", @@ -19,7 +19,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { cl val clientId by clientIdOption - execute { + apply { // region Patch client id. /** @@ -28,23 +28,23 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { cl * * @param string The string to replace the instruction with. * @param getReplacementIndex A function that returns the index of the instruction to replace - * using the [Match.StringMatch] list from the [Match]. + * using the [CompositeMatch.indices] list from the [CompositeMatch]. */ - fun Fingerprint.replaceWith( + fun CompositeMatch.replaceWith( string: String, - getReplacementIndex: List.() -> Int, + getReplacementIndex: List.() -> Int, ) = method.apply { - val replacementIndex = stringMatches!!.getReplacementIndex() + val replacementIndex = indices[0].getReplacementIndex() val clientIdRegister = getInstruction(replacementIndex).registerA replaceInstruction(replacementIndex, "const-string v$clientIdRegister, \"$string\"") } // Patch OAuth authorization. - buildAuthorizationStringFingerprint.replaceWith(clientId!!) { first().index + 4 } + buildAuthorizationStringMethodMatch.replaceWith(clientId!!) { first() + 4 } // Path basic authorization. - basicAuthorizationFingerprint.replaceWith("$clientId:") { last().index + 7 } + basicAuthorizationMethodMatch.replaceWith("$clientId:") { last() + 7 } // endregion @@ -54,7 +54,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { cl val randomName = (0..100000).random() val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)" - getUserAgentFingerprint.method.returnEarly(userAgent) + getUserAgentMethod.returnEarly(userAgent) // endregion @@ -62,7 +62,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { cl // Reddit messed up and does not append a redirect uri to the authorization url to old.reddit.com/login. // Replace old.reddit.com with www.reddit.com to fix this. - buildAuthorizationStringFingerprint.method.apply { + buildAuthorizationStringMethodMatch.method.apply { val index = indexOfFirstInstructionOrThrow { getReference()?.contains("old.reddit.com") == true } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt index 263602f730..b39e0d9ecd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt @@ -1,26 +1,33 @@ package app.revanced.patches.reddit.customclients.relayforreddit.api -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode +import org.stringtemplate.v4.compiler.Bytecode -internal fun baseClientIdFingerprint(string: String) = fingerprint { - strings("dj-xCIZQYiLbEg", string) +internal fun baseClientIdMethod(string: String) = composingFirstMethod { + instructions( + "dj-xCIZQYiLbEg"(), + string(), + ) } -internal val getLoggedInBearerTokenFingerprint = baseClientIdFingerprint("authorization_code") +internal val BytecodePatchContext.getLoggedInBearerTokenMethodMatch by baseClientIdMethod("authorization_code") -internal val getLoggedOutBearerTokenFingerprint = baseClientIdFingerprint("https://oauth.reddit.com/grants/installed_client") +internal val BytecodePatchContext.getLoggedOutBearerTokenMethodMatch by baseClientIdMethod("https://oauth.reddit.com/grants/installed_client") -internal val getRefreshTokenFingerprint = baseClientIdFingerprint("refresh_token") +internal val BytecodePatchContext.getRefreshTokenMethodMatch by baseClientIdMethod("refresh_token") -internal val loginActivityClientIdFingerprint = baseClientIdFingerprint("&duration=permanent") +internal val BytecodePatchContext.loginActivityClientIdMethodMatch by baseClientIdMethod("&duration=permanent") -internal val redditCheckDisableAPIFingerprint = fingerprint { - opcodes(Opcode.IF_EQZ) - strings("Reddit Disabled") +internal val BytecodePatchContext.redditCheckDisableAPIMethod by gettingFirstMethodDeclaratively("Reddit Disabled") { + instructions(Opcode.IF_EQZ()) } -internal val setRemoteConfigFingerprint = fingerprint { - parameters("Lcom/google/firebase/remoteconfig/FirebaseRemoteConfig;") - strings("reddit_oauth_url") +internal val BytecodePatchContext.setRemoteConfigMethod by gettingFirstMethodDeclaratively("reddit_oauth_url") { + parameterTypes("Lcom/google/firebase/remoteconfig/FirebaseRemoteConfig;") } 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..df4c381ad4 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 @@ -1,40 +1,37 @@ package app.revanced.patches.reddit.customclients.relayforreddit.api -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.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patches.reddit.customclients.spoofClientPatch +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction10t import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") { +@Suppress("unused") +val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") { clientIdOption -> compatibleWith( "free.reddit.news", "reddit.news", ) - val clientId by it + val clientId by clientIdOption - execute { + apply { // region Patch client id. - setOf( - loginActivityClientIdFingerprint, - getLoggedInBearerTokenFingerprint, - getLoggedOutBearerTokenFingerprint, - getRefreshTokenFingerprint, - ).forEach { fingerprint -> - val clientIdIndex = fingerprint.stringMatches!!.first().index - fingerprint.method.apply { - val clientIdRegister = getInstruction(clientIdIndex).registerA + listOf( + loginActivityClientIdMethodMatch, + getLoggedInBearerTokenMethodMatch, + getLoggedOutBearerTokenMethodMatch, + getRefreshTokenMethodMatch, + ).forEach { match -> + val clientIdIndex = match[0] + val clientIdRegister = match.method.getInstruction(clientIdIndex).registerA - fingerprint.method.replaceInstruction( - clientIdIndex, - "const-string v$clientIdRegister, \"$clientId\"", - ) - } + match.method.replaceInstruction(clientIdIndex, "const-string v$clientIdRegister, \"$clientId\"") } // endregion @@ -42,12 +39,11 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") { // region Patch miscellaneous. // Do not load remote config which disables OAuth login remotely. - setRemoteConfigFingerprint.method.addInstructions(0, "return-void") + setRemoteConfigMethod.returnEarly() // Prevent OAuth login being disabled remotely. - val checkIsOAuthRequestIndex = redditCheckDisableAPIFingerprint.patternMatch!!.startIndex - - redditCheckDisableAPIFingerprint.method.apply { + redditCheckDisableAPIMethod.apply { + val checkIsOAuthRequestIndex = indexOfFirstInstructionOrThrow(Opcode.IF_EQZ) val returnNextChain = getInstruction(checkIsOAuthRequestIndex).target replaceInstruction(checkIsOAuthRequestIndex, BuilderInstruction10t(Opcode.GOTO, returnNextChain)) } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt index 4ff8be4615..84d6b73a52 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt @@ -1,11 +1,11 @@ package app.revanced.patches.reddit.customclients.slide.api -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val getClientIdFingerprint = fingerprint { - custom { method, classDef -> - if (!classDef.endsWith("Credentials;")) return@custom false - - method.name == "getClientId" - } +internal val BytecodePatchContext.getClientIdMethod by gettingFirstMethodDeclaratively { + name("getClientId") + definingClass("Credentials;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt index 692bb828b0..aab8fff8f3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt @@ -8,7 +8,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://www.ccrama.me") { val clientId by clientIdOption - execute { - getClientIdFingerprint.method.returnEarly(clientId!!) + apply { + getClientIdMethod.returnEarly(clientId!!) } } 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..8db9d168cf 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 @@ -7,8 +7,8 @@ import app.revanced.util.returnEarly fun disableAdsPatch(block: BytecodePatchBuilder.() -> Unit = {}) = bytecodePatch( name = "Disable ads", ) { - execute { - isAdsEnabledFingerprint.method.returnEarly() + apply { + isAdsEnabledMethod.returnEarly(false) } block() diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt index e055493bdd..d7f1630a24 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt @@ -1,10 +1,12 @@ package app.revanced.patches.reddit.customclients.sync.ads +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.accessFlags import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val isAdsEnabledFingerprint = fingerprint { +internal val BytecodePatchContext.isAdsEnabledMethod by gettingFirstMethodDeclaratively("SyncIapHelper") { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - strings("SyncIapHelper") + returnType("Z") } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt index 53069a951a..e84f4cc9e2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt @@ -1,15 +1,15 @@ package app.revanced.patches.reddit.customclients.sync.detection.piracy -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly val disablePiracyDetectionPatch = bytecodePatch( description = "Disables detection of modified versions.", ) { - execute { - // Do not throw an error if the fingerprint is not resolved. + apply { + // Do not throw an error if the method can't be found. // This is fine because new versions of the target app do not need this patch. - piracyDetectionFingerprint.methodOrNull?.addInstruction(0, "return-void") + detectPiracyMethodOrNull?.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt index e83e914d75..a78f840aa0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt @@ -1,15 +1,24 @@ package app.revanced.patches.reddit.customclients.sync.detection.piracy -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference +import app.revanced.patcher.accessFlags +import app.revanced.patcher.custom +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.reference +import app.revanced.patcher.gettingFirstMethodDeclarativelyOrNull +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import app.revanced.patcher.type import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.Reference -internal val piracyDetectionFingerprint = fingerprint { +internal val BytecodePatchContext.detectPiracyMethodOrNull by gettingFirstMethodDeclarativelyOrNull( + "Lcom/github/javiersantos/piracychecker/PiracyChecker;", +) { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") + returnType("V") opcodes( Opcode.NEW_INSTANCE, Opcode.INVOKE_DIRECT, @@ -17,10 +26,5 @@ internal val piracyDetectionFingerprint = fingerprint { Opcode.INVOKE_DIRECT, Opcode.INVOKE_VIRTUAL, ) - custom { method, _ -> - method.implementation ?: return@custom false - method.instructions.any { - it.getReference()?.toString() == "Lcom/github/javiersantos/piracychecker/PiracyChecker;" - } - } + instructions(type("Lcom/github/javiersantos/piracychecker/PiracyChecker;")) } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt index 0bfbe74d0c..e2bc618efd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt @@ -1,6 +1,7 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.annoyances.startup -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.removeInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -14,11 +15,9 @@ val disableSyncForLemmyBottomSheetPatch = bytecodePatch( "com.laurencedawson.reddit_sync.dev"(), // Version unknown. ) - execute { - mainActivityOnCreateFingerprint.method.apply { - val showBottomSheetIndex = implementation!!.instructions.lastIndex - 1 + apply { + val showBottomSheetIndex = mainActivityOnCreateMethod.instructions.lastIndex - 1 - removeInstruction(showBottomSheetIndex) - } + mainActivityOnCreateMethod.removeInstruction(showBottomSheetIndex) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt index 21c788a89c..fdfefa38d6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.annoyances.startup -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val mainActivityOnCreateFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("MainActivity;") && method.name == "onCreate" - } +internal val BytecodePatchContext.mainActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("MainActivity;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt index c7902b1f4d..5b1044513a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt @@ -1,19 +1,26 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.api -import app.revanced.patcher.fingerprint +import app.revanced.patcher.ClassDefComposing +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.string +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val getAuthorizationStringFingerprint = fingerprint { - strings("authorize.compact?client_id") +internal val BytecodePatchContext.getAuthorizationStringMethodMatch by composingFirstMethod { + instructions(string { contains("authorize.compact?client_id") }) } -internal val getBearerTokenFingerprint = fingerprint { - strings("Basic") +internal val ClassDef.getBearerTokenMethodMatch by ClassDefComposing.composingFirstMethod { + instructions(string { contains("Basic") }) } -internal val getUserAgentFingerprint = fingerprint { - strings("android:com.laurencedawson.reddit_sync") -} +internal val BytecodePatchContext.getUserAgentMethod by gettingFirstMethod( + "android:com.laurencedawson.reddit_sync", +) -internal val imgurImageAPIFingerprint = fingerprint { - strings("https://imgur-apiv3.p.rapidapi.com/3/image") +internal val BytecodePatchContext.imgurImageAPIMethodMatch by composingFirstMethod { + instructions("https://imgur-apiv3.p.rapidapi.com/3/image"()) } 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..4422cdc103 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 @@ -1,16 +1,16 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.api -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.extensions.stringReference import app.revanced.patches.reddit.customclients.spoofClientPatch import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch import app.revanced.patches.shared.misc.string.replaceStringPatch import app.revanced.util.returnEarly 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.StringReference -import java.util.Base64 +import java.util.* +@Suppress("unused") val spoofClientPatch = spoofClientPatch( redirectUri = "http://redditsync/auth", ) { clientIdOption -> @@ -18,7 +18,7 @@ val spoofClientPatch = spoofClientPatch( disablePiracyDetectionPatch, // Redirects from SSL to WWW domain are bugged causing auth problems. // Manually rewrite the URLs to fix this. - replaceStringPatch("ssl.reddit.com", "www.reddit.com") + replaceStringPatch("ssl.reddit.com", "www.reddit.com"), ) compatibleWith( @@ -29,22 +29,20 @@ val spoofClientPatch = spoofClientPatch( val clientId by clientIdOption - execute { + apply { // region Patch client id. - getBearerTokenFingerprint.match(getAuthorizationStringFingerprint.originalClassDef).method.apply { + getAuthorizationStringMethodMatch.immutableClassDef.getBearerTokenMethodMatch.method.apply { val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8)) returnEarly("Basic $auth") - val occurrenceIndex = - getAuthorizationStringFingerprint.stringMatches!!.first().index + val occurrenceIndex = getAuthorizationStringMethodMatch[0] - getAuthorizationStringFingerprint.method.apply { - val authorizationStringInstruction = getInstruction(occurrenceIndex) - val targetRegister = (authorizationStringInstruction as OneRegisterInstruction).registerA - val reference = authorizationStringInstruction.reference as StringReference + getAuthorizationStringMethodMatch.method.apply { + val authorizationStringInstruction = getInstruction(occurrenceIndex) + val targetRegister = authorizationStringInstruction.registerA - val newAuthorizationUrl = reference.string.replace( + val newAuthorizationUrl = authorizationStringInstruction.stringReference!!.string.replace( "client_id=.*?&".toRegex(), "client_id=$clientId&", ) @@ -64,19 +62,17 @@ val spoofClientPatch = spoofClientPatch( val randomName = (0..100000).random() val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)" - getUserAgentFingerprint.method.returnEarly(userAgent) + getUserAgentMethod.returnEarly(userAgent) // endregion // region Patch Imgur API URL. - imgurImageAPIFingerprint.let { - val apiUrlIndex = it.stringMatches!!.first().index - it.method.replaceInstruction( - apiUrlIndex, - "const-string v1, \"https://api.imgur.com/3/image\"", - ) - } + val apiUrlIndex = imgurImageAPIMethodMatch[0] + imgurImageAPIMethodMatch.method.replaceInstruction( + apiUrlIndex, + "const-string v1, \"https://api.imgur.com/3/image\"", + ) // endregion } 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..ca08b5d7f6 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 @@ -1,39 +1,39 @@ 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.* +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.indexOfFirstInstruction import app.revanced.util.writeRegister import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11n +import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction - -internal val createOkHttpClientFingerprint = fingerprint { +internal val BytecodePatchContext.createOkHttpClientMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("V") - parameters() - custom { method, classDef -> - // There are four functions (each creating a client) defined in this file with very similar fingerprints. + returnType("V") + parameterTypes() + custom { + // There are four functions (each creating a client) defined in this file with very similar methods. // We're looking for the one that only creates one object (the builder) and sets client options true // (thus never reloading the register with a 0). - classDef.sourceFile == "OkHttpHelper.java" && - method.instructions.count { it.opcode == Opcode.NEW_INSTANCE } == 1 && - method.indexOfFirstInstruction { - opcode == Opcode.CONST_4 && writeRegister == 1 && (this as Instruction11n).narrowLiteral == 0 + immutableClassDef.sourceFile == "OkHttpHelper.java" && instructions.count { + it.opcode == Opcode.NEW_INSTANCE + } == 1 && indexOfFirstInstruction { + opcode == Opcode.CONST_4 && writeRegister == 1 && (this as NarrowLiteralInstruction).narrowLiteral == 0 } == -1 } } -internal val getDefaultUserAgentFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getDefaultUserAgent" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } +internal val BytecodePatchContext.getDefaultUserAgentMethod by gettingFirstMethodDeclaratively { + name("getDefaultUserAgent") + definingClass(EXTENSION_CLASS_DESCRIPTOR) } -internal val getOriginalUserAgentFingerprint = fingerprint { +internal val BytecodePatchContext.getOriginalUserAgentMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters() - custom { _, classDef -> classDef.sourceFile == "AccountSingleton.java" } + returnType { startsWith("Ljava/lang/String;") } + parameterTypes() + custom { immutableClassDef.sourceFile == "AccountSingleton.java" } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/FixRedgifsApiPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/FixRedgifsApiPatch.kt index a4f3842f9a..e4ae3a322e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/FixRedgifsApiPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/FixRedgifsApiPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.redgifs -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.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patches.reddit.customclients.INSTALL_NEW_CLIENT_METHOD import app.revanced.patches.reddit.customclients.fixRedgifsApiPatch import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.sharedExtensionPatch @@ -23,33 +23,30 @@ val fixRedgifsApi = fixRedgifsApiPatch( "com.laurencedawson.reddit_sync.dev", ) - execute { + apply { // region Patch Redgifs OkHttp3 client. - createOkHttpClientFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.name == "build" && reference.definingClass == "Lokhttp3/OkHttpClient\$Builder;" - } - val register = getInstruction(index).registerC - replaceInstruction( - index, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$INSTALL_NEW_CLIENT_METHOD - """ - ) + val index = createOkHttpClientMethod.indexOfFirstInstructionOrThrow { + val reference = getReference() + reference?.name == "build" && reference.definingClass == $$"Lokhttp3/OkHttpClient$Builder;" } + val register = createOkHttpClientMethod.getInstruction(index).registerC - getDefaultUserAgentFingerprint.method.apply { - addInstructions( - 0, - """ - invoke-static { }, ${getOriginalUserAgentFingerprint.method} - move-result-object v0 - return-object v0 - """ - ) - } + createOkHttpClientMethod.replaceInstruction( + index, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$INSTALL_NEW_CLIENT_METHOD + """ + ) + + getDefaultUserAgentMethod.addInstructions( + 0, + """ + invoke-static { }, $getOriginalUserAgentMethod + move-result-object v0 + return-object v0 + """ + ) // endregion } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt index f7287fcc39..35d97c07ba 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt @@ -1,13 +1,17 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.slink -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val linkHelperOpenLinkFingerprint = fingerprint { - strings("Link title: ") -} +internal val BytecodePatchContext.linkHelperOpenLinkMethod by gettingFirstMethod("Link title: ") -internal val setAuthorizationHeaderFingerprint = fingerprint { - returns("Ljava/util/HashMap;") - strings("Authorization", "bearer ") - custom { method, _ -> method.definingClass == "Lcom/laurencedawson/reddit_sync/singleton/a;" } +internal val BytecodePatchContext.setAuthorizationHeaderMethod by gettingFirstMethodDeclaratively( + "Authorization", + "bearer ", +) { + definingClass("Lcom/laurencedawson/reddit_sync/singleton/a;") + returnType { equals("Ljava/util/HashMap;") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt index 06ee67fe4c..de7a9f221a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt @@ -1,9 +1,9 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.slink -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patches.reddit.customclients.RESOLVE_S_LINK_METHOD import app.revanced.patches.reddit.customclients.SET_ACCESS_TOKEN_METHOD import app.revanced.patches.reddit.customclients.fixSLinksPatch @@ -21,30 +21,28 @@ val fixSLinksPatch = fixSLinksPatch( "com.laurencedawson.reddit_sync.dev", ) - execute { + apply { // region Patch navigation handler. - linkHelperOpenLinkFingerprint.method.apply { - val urlRegister = "p3" - val tempRegister = "v2" + val urlRegister = "p3" + val tempRegister = "v2" - addInstructionsWithLabels( - 0, - """ - invoke-static { $urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->$RESOLVE_S_LINK_METHOD - move-result $tempRegister - if-eqz $tempRegister, :continue - return $tempRegister - """, - ExternalLabel("continue", getInstruction(0)), - ) - } + linkHelperOpenLinkMethod.addInstructionsWithLabels( + 0, + """ + invoke-static { $urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->$RESOLVE_S_LINK_METHOD + move-result $tempRegister + if-eqz $tempRegister, :continue + return $tempRegister + """, + ExternalLabel("continue", linkHelperOpenLinkMethod.getInstruction(0)), + ) // endregion // region Patch set access token. - setAuthorizationHeaderFingerprint.method.addInstruction( + setAuthorizationHeaderMethod.addInstruction( 0, "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->$SET_ACCESS_TOKEN_METHOD", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/Fingerprints.kt index 7a64031f16..5cbbb6862f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/Fingerprints.kt @@ -1,12 +1,14 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.thumbnail -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val customImageViewLoadFingerprint = fingerprint { +internal val BytecodePatchContext.customImageViewLoadMethod by gettingFirstMethodDeclaratively { + definingClass("CustomImageView;") accessFlags(AccessFlags.PUBLIC) - parameters("Ljava/lang/String;", "Z", "Z", "I", "I") - custom { _, classDef -> - classDef.endsWith("CustomImageView;") - } + parameterTypes("Ljava/lang/String;", "Z", "Z", "I", "I") } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/FixPostThumbnailsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/FixPostThumbnailsPatch.kt index 4ac2fc3d05..f8180d52b7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/FixPostThumbnailsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/thumbnail/FixPostThumbnailsPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.thumbnail -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -12,12 +12,12 @@ val fixPostThumbnailsPatch = bytecodePatch( compatibleWith( "com.laurencedawson.reddit_sync", "com.laurencedawson.reddit_sync.pro", - "com.laurencedawson.reddit_sync.dev" + "com.laurencedawson.reddit_sync.dev", ) // Image URLs contain escaped ampersands (&), let's replace these with unescaped ones (&). - execute { - customImageViewLoadFingerprint.method.addInstructions( + apply { + customImageViewLoadMethod.addInstructions( 0, """ # url = url.replace("&", "&"); @@ -25,7 +25,7 @@ val fixPostThumbnailsPatch = bytecodePatch( const-string v1, "&" invoke-virtual { p1, v0, v1 }, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String; move-result-object p1 - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt index 4bac74de77..5aa03483de 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt @@ -1,36 +1,40 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.user -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal fun userEndpointFingerprint(source: String, accessFlags: Set? = null) = fingerprint { - strings("u/") - custom { _, classDef -> classDef.sourceFile == source } - accessFlags(*accessFlags?.toTypedArray() ?: return@fingerprint) +internal fun userEndpointMethodMatch( + source: String, + accessFlags: Set? = null, +) = composingFirstMethod { + instructions("u/"(String::contains)) + custom { immutableClassDef.sourceFile == source } + accessFlags(*accessFlags?.toTypedArray() ?: return@composingFirstMethod) } -internal val oAuthFriendRequestFingerprint = userEndpointFingerprint( +internal val BytecodePatchContext.oAuthFriendRequestMethodMatch by userEndpointMethodMatch( "OAuthFriendRequest.java", ) -internal val oAuthUnfriendRequestFingerprint = userEndpointFingerprint( +internal val BytecodePatchContext.oAuthUnfriendRequestMethodMatch by userEndpointMethodMatch( "OAuthUnfriendRequest.java", ) -internal val oAuthUserIdRequestFingerprint = userEndpointFingerprint( +internal val BytecodePatchContext.oAuthUserIdRequestMethodMatch by userEndpointMethodMatch( "OAuthUserIdRequest.java", ) -internal val oAuthUserInfoRequestFingerprint = userEndpointFingerprint( +internal val BytecodePatchContext.oAuthUserInfoRequestMethodMatch by userEndpointMethodMatch( "OAuthUserInfoRequest.java", ) -internal val oAuthSubredditInfoRequestConstructorFingerprint = userEndpointFingerprint( +internal val BytecodePatchContext.oAuthSubredditInfoRequestConstructorMethodMatch by userEndpointMethodMatch( "OAuthSubredditInfoRequest.java", setOf(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR), ) -internal val oAuthSubredditInfoRequestHelperFingerprint = userEndpointFingerprint( +internal val BytecodePatchContext.oAuthSubredditInfoRequestHelperMethodMatch by userEndpointMethodMatch( "OAuthSubredditInfoRequest.java", setOf(AccessFlags.PRIVATE, AccessFlags.STATIC), ) 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..5c0dbf9419 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 @@ -1,11 +1,10 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.user -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.extensions.stringReference import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.getReference import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.StringReference @Suppress("unused") val useUserEndpointPatch = bytecodePatch( @@ -20,22 +19,21 @@ val useUserEndpointPatch = bytecodePatch( "com.laurencedawson.reddit_sync.dev", ) - execute { + apply { arrayOf( - oAuthFriendRequestFingerprint, - oAuthSubredditInfoRequestConstructorFingerprint, - oAuthSubredditInfoRequestHelperFingerprint, - oAuthUnfriendRequestFingerprint, - oAuthUserIdRequestFingerprint, - oAuthUserInfoRequestFingerprint, - ).map { fingerprint -> - fingerprint.stringMatches!!.first().index to fingerprint.method + oAuthFriendRequestMethodMatch, + oAuthSubredditInfoRequestConstructorMethodMatch, + oAuthSubredditInfoRequestHelperMethodMatch, + oAuthUnfriendRequestMethodMatch, + oAuthUserIdRequestMethodMatch, + oAuthUserInfoRequestMethodMatch, + ).map { match -> + match[0] to match.method }.forEach { (userPathStringIndex, method) -> val userPathStringInstruction = method.getInstruction(userPathStringIndex) val userPathStringRegister = userPathStringInstruction.registerA - val fixedUserPathString = userPathStringInstruction.getReference()!! - .string.replace("u/", "user/") + val fixedUserPathString = userPathStringInstruction.stringReference!!.string.replace("u/", "user/") method.replaceInstruction( userPathStringIndex, diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/Fingerprints.kt index 9ddeaf9cf5..7f11752aee 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/video/Fingerprints.kt @@ -1,16 +1,16 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.video -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val parseRedditVideoNetworkResponseFingerprint = fingerprint { +internal val BytecodePatchContext.parseRedditVideoNetworkResponseMethodMatch by composingFirstMethod { + name("parseNetworkResponse") opcodes( Opcode.NEW_INSTANCE, Opcode.IGET_OBJECT, Opcode.INVOKE_DIRECT, Opcode.CONST_WIDE_32, ) - custom { methodDef, classDef -> - classDef.sourceFile == "RedditVideoRequest.java" && methodDef.name == "parseNetworkResponse" - } + custom { immutableClassDef.sourceFile == "RedditVideoRequest.java" } } 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..0baae8ab93 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 @@ -1,7 +1,7 @@ package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.video -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.sharedExtensionPatch import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c @@ -23,15 +23,15 @@ val fixVideoDownloadsPatch = bytecodePatch( "com.laurencedawson.reddit_sync.dev", ) - execute { - val scanResult = parseRedditVideoNetworkResponseFingerprint.patternMatch!! - val newInstanceIndex = scanResult.startIndex - val invokeDirectIndex = scanResult.endIndex - 1 + apply { + val scanResult = parseRedditVideoNetworkResponseMethodMatch.indices[0] + val newInstanceIndex = scanResult.first() + val invokeDirectIndex = scanResult.last() - 1 val buildResponseInstruction = - parseRedditVideoNetworkResponseFingerprint.method.getInstruction(invokeDirectIndex) + parseRedditVideoNetworkResponseMethodMatch.method.getInstruction(invokeDirectIndex) - parseRedditVideoNetworkResponseFingerprint.method.addInstructions( + parseRedditVideoNetworkResponseMethodMatch.method.addInstructions( newInstanceIndex + 1, """ # Get byte array from response. 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/disablescreenshotpopup/DisableScreenshotPopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt index 84ee7e2fd3..12f74f4f6d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.reddit.layout.disablescreenshotpopup -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,7 +10,7 @@ val disableScreenshotPopupPatch = bytecodePatch( ) { compatibleWith("com.reddit.frontpage") - execute { - disableScreenshotPopupFingerprint.method.addInstruction(0, "return-void") + apply { + disableScreenshotPopupMethod.addInstruction(0, "return-void") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt index 09fe16247e..3e33c784ac 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt @@ -1,15 +1,15 @@ package app.revanced.patches.reddit.layout.disablescreenshotpopup -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val disableScreenshotPopupFingerprint = fingerprint { - returns("V") - parameters("Landroidx/compose/runtime/", "I") - custom { method, classDef -> - if (!classDef.endsWith("\$ScreenshotTakenBannerKt\$lambda-1\$1;")) { - return@custom false - } - - method.name == "invoke" - } +internal val BytecodePatchContext.disableScreenshotPopupMethod by gettingFirstMethodDeclaratively { + name("invoke") + definingClass($$"$ScreenshotTakenBannerKt$lambda-1$1;") + returnType("V") + parameterTypes("Landroidx/compose/runtime/", "I") } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt index 2eac1cbe26..b7e2dc0cb4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt @@ -1,10 +1,13 @@ package app.revanced.patches.reddit.layout.premiumicon -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val hasPremiumIconAccessFingerprint = fingerprint { - returns("Z") - custom { method, classDef -> - classDef.endsWith("MyAccount;") && method.name == "isPremiumSubscriber" - } +internal val BytecodePatchContext.hasPremiumIconAccessMethod by gettingFirstMethodDeclaratively { + name("isPremiumSubscriber") + definingClass("MyAccount;") + returnType("Z") } 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..07f1eeaf4f 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 @@ -1,7 +1,7 @@ package app.revanced.patches.reddit.layout.premiumicon -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") val unlockPremiumIconsPatch = bytecodePatch( @@ -10,19 +10,7 @@ val unlockPremiumIconsPatch = bytecodePatch( ) { compatibleWith("com.reddit.frontpage") - execute { - hasPremiumIconAccessFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + hasPremiumIconAccessMethod.returnEarly(true) } } - -@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/reddit/misc/tracking/url/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt index 3381fd2bb2..e2896dc5ff 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt @@ -1,9 +1,12 @@ package app.revanced.patches.reddit.misc.tracking.url -import app.revanced.patcher.fingerprint +import app.revanced.patcher.custom +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.immutableClassDef +import app.revanced.patcher.patch.BytecodePatchContext -internal val shareLinkFormatterFingerprint = fingerprint { - custom { _, classDef -> - classDef.startsWith("Lcom/reddit/sharing/") && classDef.sourceFile == "UrlUtil.kt" - } -} \ No newline at end of file +internal val BytecodePatchContext.shareLinkFormatterMethod by gettingFirstMethodDeclaratively { + definingClass("Lcom/reddit/sharing/") + custom { immutableClassDef.sourceFile == "UrlUtil.kt" } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt index 11bc79212b..db58438184 100644 --- a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt @@ -1,21 +1,16 @@ package app.revanced.patches.reddit.misc.tracking.url -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS -import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS @Suppress("unused") -val sanitizeUrlQueryPatch = bytecodePatch( - name = PATCH_NAME_SANITIZE_SHARING_LINKS, - description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS, +val sanitizeSharingLinksPatch = bytecodePatch( + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from shared links.", ) { compatibleWith("com.reddit.frontpage") - execute { - shareLinkFormatterFingerprint.method.addInstructions( - 0, - "return-object p0", - ) + apply { + shareLinkFormatterMethod.addInstructions(0, "return-object p0") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/AddManifestPermissionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/AddManifestPermissionsPatch.kt index 35641f2e7e..6bdda5f28e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/AddManifestPermissionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/AddManifestPermissionsPatch.kt @@ -6,14 +6,13 @@ import org.w3c.dom.Element @Suppress("unused") internal val addManifestPermissionsPatch = resourcePatch { - val requiredPermissions = listOf( "android.permission.READ_PHONE_STATE", "android.permission.FOREGROUND_SERVICE_MICROPHONE", "android.permission.RECORD_AUDIO", ) - execute { + apply { document("AndroidManifest.xml").use { document -> document.getElementsByTagName("manifest").item(0).let { manifestEl -> 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..22a3e4c596 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,18 +1,13 @@ -@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 +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val permissionRequestListFingerprint = fingerprint { - strings( - "android.permission.POST_NOTIFICATIONS", - "android.permission.READ_MEDIA_AUDIO", - "android.permission.RECORD_AUDIO" - ) - custom { method, _ -> method.name == "" } +internal val BytecodePatchContext.permissionRequestListMethod by gettingFirstMethodDeclaratively( + "android.permission.POST_NOTIFICATIONS", + "android.permission.READ_MEDIA_AUDIO", + "android.permission.RECORD_AUDIO" +) { + name("") } diff --git a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatch.kt b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatch.kt index a076ca8302..e405585b19 100644 --- a/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatch.kt @@ -1,9 +1,8 @@ -@file:Suppress("unused") package app.revanced.patches.samsung.radio.misc.fix.crash -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.samsung.radio.restrictions.device.bypassDeviceChecksPatch import app.revanced.util.findInstructionIndicesReversedOrThrow @@ -13,15 +12,17 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch;" -val fixCrashPatch = bytecodePatch( - name = "Fix crashes", description = "Prevents the app from crashing because of missing system permissions." +@Suppress("unused") +val fixCrashesPatch = bytecodePatch( + name = "Fix crashes", + description = "Prevents the app from crashing because of missing system permissions.", ) { dependsOn(addManifestPermissionsPatch, bypassDeviceChecksPatch) extendWith("extensions/samsung/radio.rve") compatibleWith("com.sec.android.app.fm"("12.4.00.7", "12.3.00.13", "12.3.00.11")) - execute { - permissionRequestListFingerprint.method.apply { + apply { + permissionRequestListMethod.apply { findInstructionIndicesReversedOrThrow(Opcode.FILLED_NEW_ARRAY).forEach { filledNewArrayIndex -> val moveResultIndex = indexOfFirstInstruction(filledNewArrayIndex, Opcode.MOVE_RESULT_OBJECT) if (moveResultIndex < 0) return@forEach // No move-result-object found after the filled-new-array @@ -31,12 +32,13 @@ val fixCrashPatch = bytecodePatch( // Invoke the method from the extension addInstructions( - moveResultIndex + 1, """ + moveResultIndex + 1, + """ invoke-static { v$arrayRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->fixPermissionRequestList([Ljava/lang/String;)[Ljava/lang/String; move-result-object v$arrayRegister - """ + """, ) } } } -} \ No newline at end of file +} 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..f3b2e9f080 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 @@ -1,7 +1,7 @@ package app.revanced.patches.samsung.radio.restrictions.device -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.findFreeRegister import app.revanced.util.getReference @@ -16,40 +16,38 @@ private const val EXTENSION_CLASS_DESCRIPTOR = val bypassDeviceChecksPatch = bytecodePatch( name = "Bypass device checks", description = "Removes firmware and region blacklisting. " + - "This patch will still not allow the app to run on devices that do not have the required hardware.", + "This patch will still not allow the app to run on devices that do not have the required hardware.", ) { extendWith("extensions/samsung/radio.rve") 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 - checkDeviceFingerprint.method.apply { - // 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 - 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 - 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 - """ - ) - - // Remove the instructions before our strings - removeInstructions(0, firstStringIndex) + apply { + // Find the first string that start with "SM-", that's the list of incompatible devices. + val firstStringIndex = checkDeviceMethod.indexOfFirstInstructionOrThrow { + opcode == Opcode.CONST_STRING && + getReference()?.string?.startsWith("SM-") == true } + + // Find the following filled-new-array (or filled-new-array/range) instruction. + val filledNewArrayIndex = checkDeviceMethod.indexOfFirstInstructionOrThrow(firstStringIndex + 1) { + opcode == Opcode.FILLED_NEW_ARRAY || opcode == Opcode.FILLED_NEW_ARRAY_RANGE + } + + val resultRegister = checkDeviceMethod.findFreeRegister(filledNewArrayIndex + 1) + + // Store the array there and invoke the method that we added to the class earlier. + checkDeviceMethod.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 + """, + ) + + // Remove the instructions before our strings. + // Return false = The device is not blacklisted. + checkDeviceMethod.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..f5cb7fce10 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 @@ -1,61 +1,30 @@ package app.revanced.patches.samsung.radio.restrictions.device -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 +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil -internal val checkDeviceFingerprint = fingerprint { - returns("Z") - custom { method, _ -> - /* Check for methods call to: - - Landroid/os/SemSystemProperties;->getSalesCode()Ljava/lang/String; - - Landroid/os/SemSystemProperties;->getCountryIso()Ljava/lang/String; - */ - - val impl = method.implementation ?: return@custom false - - // Track which target methods we've found - val foundMethods = mutableSetOf() - - // Scan method instructions for calls to our target methods - for (instr in impl.instructions) { - val ref = instr.getReference() ?: continue - val mc = fromMethodReference(ref) ?: continue - - if (mc == MethodCall.GetSalesCode || mc == MethodCall.GetCountryIso) { - foundMethods.add(mc) - - // If we found both methods, return success - if (foundMethods.size == 2) { - return@custom true - } - } - } - - // Only match if both methods are present - return@custom false - } +internal val BytecodePatchContext.checkDeviceMethod by gettingFirstMethodDeclaratively { + returnType("Z") + instructions( + predicates = unorderedAllOf( + method { MethodUtil.methodSignaturesMatch(getSalesCodeMethodReference, this) }, + method { MethodUtil.methodSignaturesMatch(getCountryIsoMethodReference, this) } + )) } -// Information about method calls we want to replace -private enum class MethodCall( - override val definedClassName: String, - override val methodName: String, - override val methodParams: Array, - override val returnType: String, -) : IMethodCall { - GetSalesCode( - "Landroid/os/SemSystemProperties;", - "getSalesCode", - arrayOf(), - "Ljava/lang/String;", - ), - GetCountryIso( - "Landroid/os/SemSystemProperties;", - "getCountryIso", - arrayOf(), - "Ljava/lang/String;", - ), -} \ No newline at end of file +val getSalesCodeMethodReference = ImmutableMethodReference( + "Landroid/os/SemSystemProperties;", + "getSalesCode", + emptyList(), + "Ljava/lang/String;", +) + +val getCountryIsoMethodReference = ImmutableMethodReference( + "Landroid/os/SemSystemProperties;", + "getCountryIso", + emptyList(), + "Ljava/lang/String;", +) + diff --git a/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt index f7efe3103f..f33929f1e0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt @@ -1,12 +1,14 @@ package app.revanced.patches.serviceportalbund.detection.root +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val rootDetectionFingerprint = fingerprint { +internal val BytecodePatchContext.rootDetectionMethod by gettingFirstMethodDeclaratively { + definingClass("/DeviceIntegrityCheck;") accessFlags(AccessFlags.PUBLIC) - returns("V") - custom { _, classDef -> - classDef.endsWith("/DeviceIntegrityCheck;") - } -} \ No newline at end of file + returnType("V") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt index 0c9fd90400..244af68213 100644 --- a/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt @@ -1,18 +1,16 @@ package app.revanced.patches.serviceportalbund.detection.root -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION -import app.revanced.patches.shared.PATCH_NAME_REMOVE_ROOT_DETECTION +import app.revanced.util.returnEarly @Suppress("unused") -val rootDetectionPatch = bytecodePatch( - name = PATCH_NAME_REMOVE_ROOT_DETECTION, - description = PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION +val removeRootDetectionPatch = bytecodePatch( + name = "Remove root detection", + description = "Removes the check for root permissions and unlocked bootloader.", ) { compatibleWith("at.gv.bka.serviceportal") - execute { - rootDetectionFingerprint.method.addInstruction(0, "return-void") + apply { + rootDetectionMethod.returnEarly() } } 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..035523a62d 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,13 @@ package app.revanced.patches.shared -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext -internal val castContextFetchFingerprint = fingerprint { - strings("Error fetching CastContext.") -} +internal val BytecodePatchContext.castContextFetchMethod by gettingFirstMethodDeclaratively( + "Error fetching CastContext." +) -internal val primeMethodFingerprint = fingerprint { - strings("com.google.android.GoogleCamera", "com.android.vending") -} +internal val BytecodePatchContext.primeMethod by gettingFirstMethodDeclaratively( + "com.android.vending", + "com.google.android.GoogleCamera" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/SharedPatchNames.kt b/patches/src/main/kotlin/app/revanced/patches/shared/SharedPatchNames.kt deleted file mode 100644 index 44f6bbca76..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/shared/SharedPatchNames.kt +++ /dev/null @@ -1,16 +0,0 @@ -package app.revanced.patches.shared - -// -// Names and descriptions used by different patches implementing the same feature. -// - -internal const val PATCH_NAME_REMOVE_ROOT_DETECTION = "Remove root detection" -internal const val PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION = "Removes the check for root permissions and unlocked bootloader." - -internal const val PATCH_NAME_SANITIZE_SHARING_LINKS = "Sanitize sharing links" -internal const val PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS = "Removes the tracking query parameters from shared links." - -internal const val PATCH_NAME_CHANGE_LINK_SHARING_DOMAIN = "Change link sharing domain" -internal const val PATCH_DESCRIPTION_CHANGE_LINK_SHARING_DOMAIN = "Replaces the domain name of shared links." - -internal const val PATCH_NAME_HIDE_NAVIGATION_BUTTONS = "Hide navigation buttons" diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/AddBrandLicensePatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/AddBrandLicensePatch.kt index 629a53e2e8..933af78824 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/AddBrandLicensePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/AddBrandLicensePatch.kt @@ -10,7 +10,7 @@ import java.nio.file.Files * This patch must be a dependency for all patches that add ReVanced branding to the target app. */ internal val addBrandLicensePatch = rawResourcePatch { - execute { + apply { val brandingLicenseFileName = "LICENSE_REVANCED.TXT" val inputFileStream = inputStreamFromBundledResource( diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt index a2b88d7f1c..750dee5fdf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt @@ -1,32 +1,17 @@ package app.revanced.patches.shared.layout.branding -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.ResourcePatchBuilder -import app.revanced.patcher.patch.ResourcePatchContext -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch -import app.revanced.patcher.patch.stringOption +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.patch.* 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.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen import app.revanced.patches.shared.misc.settings.preference.ListPreference -import app.revanced.util.ResourceGroup +import app.revanced.util.* import app.revanced.util.Utils.trimIndentMultiline -import app.revanced.util.addInstructionsAtControlFlowLabel -import app.revanced.util.copyResources -import app.revanced.util.findElementByAttributeValueOrThrow -import app.revanced.util.findInstructionIndicesReversedOrThrow -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow -import app.revanced.util.removeFromParent -import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference @@ -42,13 +27,13 @@ private val mipmapDirectories = mapOf( "mipmap-hdpi" to "162x162 px", "mipmap-xhdpi" to "216x216 px", "mipmap-xxhdpi" to "324x324 px", - "mipmap-xxxhdpi" to "432x432 px" + "mipmap-xxxhdpi" to "432x432 px", ) private val iconStyleNames = arrayOf( "rounded", "minimal", - "scaled" + "scaled", ) private const val ORIGINAL_USER_ICON_STYLE_NAME = "original" @@ -62,10 +47,11 @@ private const val NOTIFICATION_ICON_NAME = "revanced_notification_icon" private val USER_CUSTOM_ADAPTIVE_FILE_NAMES = arrayOf( "$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png", - "$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png" + "$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png", ) -private const val USER_CUSTOM_MONOCHROME_FILE_NAME = "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml" +private const val USER_CUSTOM_MONOCHROME_FILE_NAME = + "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml" private const val USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME = "${NOTIFICATION_ICON_NAME}_$CUSTOM_USER_ICON_STYLE_NAME.xml" internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/CustomBrandingPatch;" @@ -80,26 +66,24 @@ internal fun baseCustomBrandingPatch( originalAppPackageName: String, isYouTubeMusic: Boolean, numberOfPresetAppNames: Int, - mainActivityOnCreateFingerprint: Fingerprint, + getMainActivityOnCreate: BytecodePatchContext.() -> MutableMethod, mainActivityName: String, activityAliasNameWithIntents: String, preferenceScreen: BasePreferenceScreen.Screen, block: ResourcePatchBuilder.() -> Unit, - executeBlock: ResourcePatchContext.() -> Unit = {} -): ResourcePatch = resourcePatch( + executeBlock: ResourcePatchContext.() -> Unit = {}, +) = resourcePatch( name = "Custom branding", description = "Adds options to change the app icon and app name. " + - "Branding cannot be changed for mounted (root) installations." + "Branding cannot be changed for mounted (root) installations.", ) { val customName by stringOption( - key = "customName", - title = "App name", - description = "Custom app name." + name = "App name", + description = "Custom app name.", ) val customIcon by stringOption( - key = "customIcon", - title = "Custom icon", + name = "Custom icon", description = """ Folder with images to use as a custom icon. @@ -115,7 +99,7 @@ internal fun baseCustomBrandingPatch( Optionally, the path contains a 'drawable' folder with any of the monochrome icon files: $USER_CUSTOM_MONOCHROME_FILE_NAME $USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME - """.trimIndentMultiline() + """.trimIndentMultiline(), ) block() @@ -125,15 +109,15 @@ internal fun baseCustomBrandingPatch( resourceMappingPatch, addBrandLicensePatch, bytecodePatch { - execute { - mainActivityOnCreateFingerprint.method.addInstruction( + apply { + getMainActivityOnCreate().addInstruction( 0, - "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setBranding()V" + "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setBranding()V", ) - numberOfPresetAppNamesExtensionFingerprint.method.returnEarly(numberOfPresetAppNames) + numberOfPresetAppNamesExtensionMethod.returnEarly(numberOfPresetAppNames) - notificationFingerprint.method.apply { + notificationMethod.apply { val getBuilderIndex = if (isYouTubeMusic) { // YT Music the field is not a plain object type. indexOfFirstInstructionOrThrow { @@ -144,7 +128,7 @@ internal fun baseCustomBrandingPatch( val builderCastIndex = indexOfFirstInstructionOrThrow { val reference = getReference() opcode == Opcode.CHECK_CAST && - reference?.type == "Landroid/app/Notification\$Builder;" + reference?.type == "Landroid/app/Notification\$Builder;" } indexOfFirstInstructionReversedOrThrow(builderCastIndex) { getReference()?.type == "Ljava/lang/Object;" @@ -155,7 +139,7 @@ internal fun baseCustomBrandingPatch( .getReference() findInstructionIndicesReversedOrThrow( - Opcode.RETURN_VOID + Opcode.RETURN_VOID, ).forEach { index -> addInstructionsAtControlFlowLabel( index, @@ -164,7 +148,7 @@ internal fun baseCustomBrandingPatch( iget-object v0, v0, $builderFieldName check-cast v0, Landroid/app/Notification${'$'}Builder; invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V - """ + """, ) } } @@ -172,32 +156,32 @@ internal fun baseCustomBrandingPatch( }, ) - finalize { + afterDependents { // Can only check if app is root installation by checking if change package name patch is in use. - // and can only do that in the finalize block here. - // The UI preferences cannot be selectively added here, because the settings finalize block + // and can only do that in the afterDependents block here. + // The UI preferences cannot be selectively added here, because the settings afterDependents block // may have already run and the settings are already wrote to file. // Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes), // and the non-functional in-app settings are removed on app startup by extension code. if (customName != null || customIcon != null) { if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) { Logger.getLogger(this::class.java.name).warning( - "Custom branding does not work with root installation. No changes applied." + "Custom branding does not work with root installation. No changes applied.", ) } } } - execute { + apply { addResources("shared", "layout.branding.baseCustomBrandingPatch") addResources(addResourcePatchName, "layout.branding.customBrandingPatch") preferenceScreen.addPreferences( - if (customName != null ) { + if (customName != null) { ListPreference( key = "revanced_custom_branding_name", entriesKey = "revanced_custom_branding_name_custom_entries", - entryValuesKey = "revanced_custom_branding_name_custom_entry_values" + entryValuesKey = "revanced_custom_branding_name_custom_entry_values", ) } else { ListPreference("revanced_custom_branding_name") @@ -206,11 +190,11 @@ internal fun baseCustomBrandingPatch( ListPreference( key = "revanced_custom_branding_icon", entriesKey = "revanced_custom_branding_icon_custom_entries", - entryValuesKey = "revanced_custom_branding_icon_custom_entry_values" + entryValuesKey = "revanced_custom_branding_icon_custom_entry_values", ) } else { ListPreference("revanced_custom_branding_icon") - } + }, ) val useCustomName = customName != null @@ -227,8 +211,8 @@ internal fun baseCustomBrandingPatch( ), ResourceGroup( "mipmap-anydpi", - "$LAUNCHER_RESOURCE_NAME_PREFIX$style.xml" - ) + "$LAUNCHER_RESOURCE_NAME_PREFIX$style.xml", + ), ) } @@ -237,19 +221,19 @@ internal fun baseCustomBrandingPatch( // Push notification 'small' icon. ResourceGroup( "drawable", - "$NOTIFICATION_ICON_NAME.xml" + "$NOTIFICATION_ICON_NAME.xml", ), // Copy template user icon, because the aliases must be added even if no user icon is provided. ResourceGroup( "drawable", USER_CUSTOM_MONOCHROME_FILE_NAME, - USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME + USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME, ), ResourceGroup( "mipmap-anydpi", - "$LAUNCHER_RESOURCE_NAME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml" - ) + "$LAUNCHER_RESOURCE_NAME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml", + ), ) // Copy template icon files. @@ -260,7 +244,7 @@ internal fun baseCustomBrandingPatch( dpi, "$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png", "$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png", - ) + ), ) } @@ -272,7 +256,7 @@ internal fun baseCustomBrandingPatch( appNameIndex: Int, useCustomName: Boolean, enabled: Boolean, - intents: NodeList + intents: NodeList, ): Element { val label = if (useCustomName) { if (customName == null) { @@ -309,7 +293,7 @@ internal fun baseCustomBrandingPatch( } else { for (i in 0 until intents.length) { alias.appendChild( - intents.item(i).cloneNode(true) + intents.item(i).cloneNode(true), ) } } @@ -320,7 +304,7 @@ internal fun baseCustomBrandingPatch( val application = document.getElementsByTagName("application").item(0) as Element val intentFilters = document.childNodes.findElementByAttributeValueOrThrow( "android:name", - activityAliasNameWithIntents + activityAliasNameWithIntents, ).childNodes // The YT application name can appear in some places along side the system @@ -329,10 +313,10 @@ internal fun baseCustomBrandingPatch( // use a custom name for this situation to disambiguate which app is which. application.setAttribute( "android:label", - "@string/revanced_custom_branding_name_entry_2" + "@string/revanced_custom_branding_name_entry_2", ) - for (appNameIndex in 1 .. numberOfPresetAppNames) { + for (appNameIndex in 1..numberOfPresetAppNames) { fun aliasName(name: String): String = ".revanced_" + name + '_' + appNameIndex val useCustomNameLabel = (useCustomName && appNameIndex == numberOfPresetAppNames) @@ -345,12 +329,12 @@ internal fun baseCustomBrandingPatch( appNameIndex = appNameIndex, useCustomName = useCustomNameLabel, enabled = (appNameIndex == 1), - intentFilters - ) + intentFilters, + ), ) // Bundled icons. - iconStyleNames.forEachIndexed { index, style -> + iconStyleNames.forEach { style -> application.appendChild( createAlias( aliasName = aliasName(style), @@ -358,8 +342,8 @@ internal fun baseCustomBrandingPatch( appNameIndex = appNameIndex, useCustomName = useCustomNameLabel, enabled = false, - intentFilters - ) + intentFilters, + ), ) } @@ -378,8 +362,8 @@ internal fun baseCustomBrandingPatch( appNameIndex = appNameIndex, useCustomName = useCustomNameLabel, enabled = false, - intentFilters - ) + intentFilters, + ), ) } @@ -387,7 +371,7 @@ internal fun baseCustomBrandingPatch( // can be shown in the launcher. Can only be done after adding the new aliases. intentFilters.findElementByAttributeValueOrThrow( "android:name", - "android.intent.action.MAIN" + "android.intent.action.MAIN", ).removeFromParent() } @@ -399,13 +383,13 @@ internal fun baseCustomBrandingPatch( if (!iconPathFile.exists()) { throw PatchException( - "The custom icon path cannot be found: " + iconPathFile.absolutePath + "The custom icon path cannot be found: " + iconPathFile.absolutePath, ) } if (!iconPathFile.isDirectory) { throw PatchException( - "The custom icon path must be a folder: " + iconPathFile.absolutePath + "The custom icon path must be a folder: " + iconPathFile.absolutePath, ) } @@ -413,8 +397,8 @@ internal fun baseCustomBrandingPatch( var copiedFiles = false // For each source folder, copy the files to the target resource directories. - iconPathFile.listFiles { - file -> file.isDirectory && file.name in mipmapDirectories + iconPathFile.listFiles { file -> + file.isDirectory && file.name in mipmapDirectories }!!.forEach { dpiSourceFolder -> val targetDpiFolder = resourceDirectory.resolve(dpiSourceFolder.name) if (!targetDpiFolder.exists()) { @@ -427,8 +411,10 @@ internal fun baseCustomBrandingPatch( }!! if (customFiles.isNotEmpty() && customFiles.size != USER_CUSTOM_ADAPTIVE_FILE_NAMES.size) { - throw PatchException("Must include all required icon files " + - "but only found " + customFiles.map { it.name }) + throw PatchException( + "Must include all required icon files " + + "but only found " + customFiles.map { it.name }, + ) } customFiles.forEach { imgSourceFile -> @@ -442,23 +428,25 @@ internal fun baseCustomBrandingPatch( // Copy monochrome and small notification icon if it provided. arrayOf( USER_CUSTOM_MONOCHROME_FILE_NAME, - USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME + USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME, ).forEach { fileName -> val relativePath = "drawable/$fileName" val file = iconPathFile.resolve(relativePath) if (file.exists()) { file.copyTo( target = resourceDirectory.resolve(relativePath), - overwrite = true + overwrite = true, ) copiedFiles = true } } if (!copiedFiles) { - throw PatchException("Expected to find directories and files: " - + USER_CUSTOM_ADAPTIVE_FILE_NAMES.contentToString() - + "\nBut none were found in the provided option file path: " + iconPathFile.absolutePath) + throw PatchException( + "Expected to find directories and files: " + + USER_CUSTOM_ADAPTIVE_FILE_NAMES.contentToString() + + "\nBut none were found in the provided option file path: " + iconPathFile.absolutePath, + ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt index 8e99078d5c..074bff4f28 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt @@ -1,21 +1,22 @@ package app.revanced.patches.shared.layout.branding -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val numberOfPresetAppNamesExtensionFingerprint = fingerprint { +internal val BytecodePatchContext.numberOfPresetAppNamesExtensionMethod by gettingFirstMethodDeclaratively { + name("numberOfPresetAppNames") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("I") - parameters() - custom { method, classDef -> - method.name == "numberOfPresetAppNames" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("I") + parameterTypes() } -// A much simpler fingerprint exists that can set the small icon (contains string "414843287017"), -// but that has limited usage and this fingerprint allows changing any part of the notification. -internal val notificationFingerprint = fingerprint { +// A much simpler method exists that can set the small icon (contains string "414843287017"), +// but that has limited usage and this one allows changing any part of the notification. +internal val BytecodePatchContext.notificationMethod by gettingFirstMethodDeclaratively( + "key_action_priority", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("L") - strings("key_action_priority") + parameterTypes("L") } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/hide/general/HideLayoutComponentsPatch.kt index 53269677a4..aa13dc7e0c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/hide/general/HideLayoutComponentsPatch.kt @@ -11,11 +11,12 @@ import app.revanced.patches.shared.misc.settings.preference.InputType 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 kotlin.collections.toTypedArray internal fun hideLayoutComponentsPatch( - lithoFilterPatch: Patch<*>, - settingsPatch: Patch<*>, - additionalDependencies: Set> = emptySet(), + lithoFilterPatch: Patch, + settingsPatch: Patch, + additionalDependencies: Set = emptySet(), filterClasses: Set, vararg compatibleWithPackages: Pair?>, executeBlock: BytecodePatchContext.() -> Unit = {}, @@ -32,7 +33,7 @@ internal fun hideLayoutComponentsPatch( compatibleWith(packages = compatibleWithPackages) - execute { + apply { addResources("shared", "layout.hide.general.hideLayoutComponentsPatch") PreferenceScreen.GENERAL.addPreferences( @@ -46,9 +47,7 @@ internal fun hideLayoutComponentsPatch( ), ) - filterClasses.forEach { className -> - addLithoFilter(className) - } + filterClasses.forEach { className -> addLithoFilter(className) } executeBlock() } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/BaseThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/BaseThemePatch.kt index 40fb779806..cd298fbb86 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/BaseThemePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/BaseThemePatch.kt @@ -1,13 +1,8 @@ package app.revanced.patches.shared.layout.theme -import app.revanced.patcher.patch.BytecodePatchBuilder -import app.revanced.patcher.patch.BytecodePatchContext -import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.patch.resourcePatch -import app.revanced.patcher.patch.stringOption +import app.revanced.patcher.patch.* import app.revanced.util.childElementsSequence -import java.util.Locale +import java.util.* internal const val THEME_COLOR_OPTION_DESCRIPTION = "Can be a hex color (#RRGGBB) or a color resource reference." @@ -56,7 +51,8 @@ internal fun validateColorName(colorString: String): Boolean { * Dark theme color option for YouTube and YT Music Theme patches. */ internal val darkThemeBackgroundColorOption = stringOption( - key = "darkThemeBackgroundColor", + name = "Dark theme background color", + description = THEME_COLOR_OPTION_DESCRIPTION, default = "@android:color/black", values = mapOf( "Pure black" to "@android:color/black", @@ -69,9 +65,7 @@ internal val darkThemeBackgroundColorOption = stringOption( "Dark yellow" to "#282900", "Dark orange" to "#291800", "Dark red" to "#290000", - ), - title = "Dark theme background color", - description = THEME_COLOR_OPTION_DESCRIPTION + ) ) /** @@ -92,7 +86,7 @@ internal fun baseThemePatch( dependsOn(lithoColorHookPatch) - execute { + apply { executeBlock() lithoColorOverrideHook(extensionClassDescriptor, "getValue") @@ -104,10 +98,9 @@ internal fun baseThemeResourcePatch( lightColorNames: Set = THEME_DEFAULT_LIGHT_COLOR_NAMES, lightColorReplacement: (() -> String)? = null ) = resourcePatch { - - execute { + apply { // After patch option validators are fixed https://github.com/ReVanced/revanced-patcher/issues/372 - // This should changed to a patch option validator. + // This should be changed to a patch option validator. val darkColor by darkThemeBackgroundColorOption if (!validateColorName(darkColor!!)) { throw PatchException("Invalid dark theme color: $darkColor") 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..c3c7e3d4c0 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,24 +1,40 @@ package app.revanced.patches.shared.layout.theme -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val lithoOnBoundsChangeFingerprint = fingerprint { +internal val BytecodePatchContext.lithoOnBoundsChangeMethodMatch by composingFirstMethod { + name("onBoundsChange") 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, - ) - custom { method, _ -> - method.name == "onBoundsChange" + returnType("V") + parameterTypes("Landroid/graphics/Rect;") + + lateinit var methodDefiningClass: String + custom { + methodDefiningClass = definingClass + true } + + instructions( + allOf( + Opcode.IPUT_OBJECT(), + field { type == "Landroid/graphics/Path;" && definingClass == methodDefiningClass }, + ), + afterAtMost( + 5, + method { returnType == "Z" && name == "isStateful" && definingClass == methodDefiningClass }, + ), + afterAtMost( + 5, + allOf( + Opcode.IGET_OBJECT(), + field { type == "Landroid/graphics/Paint;" && definingClass == methodDefiningClass }, + ), + ), + after( + method { toString() == "Landroid/graphics/Paint;->setColor(I)V" }, + ), + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/LithoColorHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/LithoColorHookPatch.kt index bfa1259a02..6844d9db4e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/LithoColorHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/theme/LithoColorHookPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.shared.layout.theme -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch lateinit var lithoColorOverrideHook: (targetMethodClass: String, targetMethodName: String) -> Unit @@ -10,16 +10,16 @@ val lithoColorHookPatch = bytecodePatch( description = "Adds a hook to set color of Litho components.", ) { - execute { - var insertionIndex = lithoOnBoundsChangeFingerprint.patternMatch!!.endIndex - 1 + apply { + var insertionIndex = lithoOnBoundsChangeMethodMatch[-1] - 1 lithoColorOverrideHook = { targetMethodClass, targetMethodName -> - lithoOnBoundsChangeFingerprint.method.addInstructions( + lithoOnBoundsChangeMethodMatch.method.addInstructions( insertionIndex, """ invoke-static { p1 }, $targetMethodClass->$targetMethodName(I)I move-result p1 - """ + """, ) insertionIndex += 2 } 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..50f48aaf2f 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,26 +1,19 @@ package app.revanced.patches.shared.misc.audio -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val formatStreamModelToStringFingerprint = fingerprint { +internal val BytecodePatchContext.formatStreamModelToStringMethodMatch by composingFirstMethod { + name("toString") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - custom { method, _ -> - method.name == "toString" - } - strings( - // Strings are partial matches. - "isDefaultAudioTrack=", - "audioTrackId=" + returnType("Ljava/lang/String;") + instructions( + "isDefaultAudioTrack="(String::contains), + "audioTrackId="(String::contains), ) } -internal const val AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG = 45666189L - -internal val selectAudioStreamFingerprint = fingerprint { - literal { - AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG - } +internal val BytecodePatchContext.selectAudioStreamMethodMatch by composingFirstMethod { + instructions(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..6360709519 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 @@ -1,15 +1,15 @@ package app.revanced.patches.shared.misc.audio -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableField.Companion.toMutable +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.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.MutableField.Companion.toMutable -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.settings.preference.BasePreferenceScreen @@ -35,120 +35,120 @@ internal fun forceOriginalAudioPatch( block: BytecodePatchBuilder.() -> Unit = {}, executeBlock: BytecodePatchContext.() -> Unit = {}, fixUseLocalizedAudioTrackFlag: BytecodePatchContext.() -> Boolean, - mainActivityOnCreateFingerprint: Fingerprint, + getMainActivityOnCreateMethod: BytecodePatchContext.() -> MutableMethod, subclassExtensionClassDescriptor: String, - preferenceScreen: BasePreferenceScreen.Screen + preferenceScreen: BasePreferenceScreen.Screen, ) = bytecodePatch( name = "Force original audio", description = "Adds an option to always use the original audio track.", ) { - block() dependsOn(addResourcesPatch) - execute { + apply { addResources("shared", "misc.audio.forceOriginalAudioPatch") preferenceScreen.addPreferences( SwitchPreference( key = "revanced_force_original_audio", - tag = "app.revanced.extension.shared.settings.preference.ForceOriginalAudioSwitchPreference" - ) + tag = "app.revanced.extension.shared.settings.preference.ForceOriginalAudioSwitchPreference", + ), ) - mainActivityOnCreateFingerprint.method.addInstruction( + getMainActivityOnCreateMethod().addInstruction( 0, - "invoke-static { }, $subclassExtensionClassDescriptor->setEnabled()V" + "invoke-static { }, $subclassExtensionClassDescriptor->setEnabled()V", ) // Disable feature flag that ignores the default track flag // and instead overrides to the user region language. if (fixUseLocalizedAudioTrackFlag()) { - selectAudioStreamFingerprint.method.insertLiteralOverride( - AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z" + selectAudioStreamMethodMatch.method.insertLiteralOverride( + selectAudioStreamMethodMatch[0], + "$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z", ) } - formatStreamModelToStringFingerprint.let { - val isDefaultAudioTrackMethod = it.originalMethod.findMethodFromToString("isDefaultAudioTrack=") - val audioTrackDisplayNameMethod = it.originalMethod.findMethodFromToString("audioTrackDisplayName=") - val audioTrackIdMethod = it.originalMethod.findMethodFromToString("audioTrackId=") + val isDefaultAudioTrackMethod = + formatStreamModelToStringMethodMatch.immutableMethod.findMethodFromToString("isDefaultAudioTrack=") + val audioTrackDisplayNameMethod = + formatStreamModelToStringMethodMatch.immutableMethod.findMethodFromToString("audioTrackDisplayName=") + val audioTrackIdMethod = + formatStreamModelToStringMethodMatch.immutableMethod.findMethodFromToString("audioTrackId=") - it.classDef.apply { - // Add a new field to store the override. - val helperFieldName = "patch_isDefaultAudioTrackOverride" - fields.add( - ImmutableField( - type, - helperFieldName, - "Ljava/lang/Boolean;", - // Boolean is a 100% immutable class (all fields are final) - // and safe to write to a shared field without volatile/synchronization, - // but without volatile the field can show stale data - // and the same field is calculated more than once by different threads. - AccessFlags.PRIVATE.value or AccessFlags.VOLATILE.value, - null, - null, - null - ).toMutable() + formatStreamModelToStringMethodMatch.classDef.apply { + // Add a new field to store the override. + val helperFieldName = "patch_isDefaultAudioTrackOverride" + fields.add( + ImmutableField( + type, + helperFieldName, + "Ljava/lang/Boolean;", + // Boolean is a 100% immutable class (all fields are final) + // and safe to write to a shared field without volatile/synchronization, + // but without volatile the field can show stale data + // and the same field is calculated more than once by different threads. + AccessFlags.PRIVATE.value or AccessFlags.VOLATILE.value, + null, + null, + null, + ).toMutable(), + ) + + // Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed. + val helperMethodClass = type + val helperMethodName = "patch_isDefaultAudioTrack" + val helperMethod = ImmutableMethod( + helperMethodClass, + helperMethodName, + listOf(ImmutableMethodParameter("Z", null, null)), + "Z", + AccessFlags.PRIVATE.value, + null, + null, + MutableMethodImplementation(6), + ).toMutable().apply { + addInstructionsWithLabels( + 0, + """ + iget-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean; + if-eqz v0, :call_extension + invoke-virtual { v0 }, Ljava/lang/Boolean;->booleanValue()Z + move-result v3 + return v3 + + :call_extension + invoke-virtual { p0 }, $audioTrackIdMethod + move-result-object v1 + + invoke-virtual { p0 }, $audioTrackDisplayNameMethod + move-result-object v2 + + invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z + move-result v3 + + invoke-static { v3 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean; + move-result-object v0 + iput-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean; + return v3 + """, ) + } + methods.add(helperMethod) - // Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed. - val helperMethodClass = type - val helperMethodName = "patch_isDefaultAudioTrack" - val helperMethod = ImmutableMethod( - helperMethodClass, - helperMethodName, - listOf(ImmutableMethodParameter("Z", null, null)), - "Z", - AccessFlags.PRIVATE.value, - null, - null, - MutableMethodImplementation(6), - ).toMutable().apply { - addInstructionsWithLabels( - 0, - """ - iget-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean; - if-eqz v0, :call_extension - invoke-virtual { v0 }, Ljava/lang/Boolean;->booleanValue()Z - move-result v3 - return v3 - - :call_extension - invoke-virtual { p0 }, $audioTrackIdMethod - move-result-object v1 - - invoke-virtual { p0 }, $audioTrackDisplayNameMethod - move-result-object v2 - - invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z - move-result v3 - - invoke-static { v3 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean; - move-result-object v0 - iput-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean; - return v3 - """ - ) - } - methods.add(helperMethod) + // Modify isDefaultAudioTrack() to call extension helper method. + isDefaultAudioTrackMethod.apply { + val index = indexOfFirstInstructionOrThrow(Opcode.RETURN) + val register = getInstruction(index).registerA - // Modify isDefaultAudioTrack() to call extension helper method. - isDefaultAudioTrackMethod.apply { - val index = indexOfFirstInstructionOrThrow(Opcode.RETURN) - val register = getInstruction(index).registerA - - addInstructions( - index, - """ + addInstructions( + index, + """ invoke-direct { p0, v$register }, $helperMethodClass->$helperMethodName(Z)Z move-result v$register - """ - ) - } + """, + ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt index 73fcb1cc24..dafda23bdd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt @@ -1,13 +1,15 @@ package app.revanced.patches.shared.misc.checks import android.os.Build.* -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.com.android.tools.smali.dexlib2.iface.value.MutableEncodedValue +import app.revanced.com.android.tools.smali.dexlib2.iface.value.MutableLongEncodedValue +import app.revanced.com.android.tools.smali.dexlib2.iface.value.MutableStringEncodedValue +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableClassDef +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableLongEncodedValue -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableStringEncodedValue import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import com.android.tools.smali.dexlib2.immutable.value.ImmutableLongEncodedValue @@ -21,8 +23,8 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/checks/CheckEnvironmentPatch;" fun checkEnvironmentPatch( - mainActivityOnCreateFingerprint: Fingerprint, - extensionPatch: Patch<*>, + getMainActivityOnCreateMethod: BytecodePatchContext.() -> MutableMethod, + extensionPatch: Patch, vararg compatiblePackages: String, ) = bytecodePatch( description = "Checks, if the application was patched by, otherwise warns the user.", @@ -34,24 +36,24 @@ fun checkEnvironmentPatch( addResourcesPatch, ) - execute { + apply { addResources("shared", "misc.checks.checkEnvironmentPatch") fun setPatchInfo() { - fun Fingerprint.setClassFields(vararg fieldNameValues: Pair) { + fun MutableClassDef.setClassFields(vararg fieldNameValues: Pair) { val fieldNameValueMap = mapOf(*fieldNameValues) - classDef.fields.forEach { field -> + fields.forEach { field -> field.initialValue = fieldNameValueMap[field.name] ?: return@forEach } } - patchInfoFingerprint.setClassFields( + patchInfoClassDef.setClassFields( "PATCH_TIME" to System.currentTimeMillis().encoded, ) fun setBuildInfo() { - patchInfoBuildFingerprint.setClassFields( + patchInfoBuildClassDef.setClassFields( "PATCH_BOARD" to BOARD.encodedAndHashed, "PATCH_BOOTLOADER" to BOOTLOADER.encodedAndHashed, "PATCH_BRAND" to BRAND.encodedAndHashed, @@ -82,7 +84,7 @@ fun checkEnvironmentPatch( } } - fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction( + fun invokeCheck() = getMainActivityOnCreateMethod().addInstruction( 0, "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt index 0eabd2f547..237344acb2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt @@ -1,11 +1,12 @@ package app.revanced.patches.shared.misc.checks -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstClassDefDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext -internal val patchInfoFingerprint = fingerprint { - custom { _, classDef -> classDef.type == "Lapp/revanced/extension/shared/checks/PatchInfo;" } -} +internal val BytecodePatchContext.patchInfoClassDef by gettingFirstClassDefDeclaratively( + "Lapp/revanced/extension/shared/checks/PatchInfo;" +) -internal val patchInfoBuildFingerprint = fingerprint { - custom { _, classDef -> classDef.type == "Lapp/revanced/extension/shared/checks/PatchInfo\$Build;" } -} +internal val BytecodePatchContext.patchInfoBuildClassDef by gettingFirstClassDefDeclaratively( + $$"Lapp/revanced/extension/shared/checks/PatchInfo$Build;" +) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/EnableDebuggingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/EnableDebuggingPatch.kt index dd00a6ebac..aa8bb88bf8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/EnableDebuggingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/EnableDebuggingPatch.kt @@ -1,7 +1,8 @@ package app.revanced.patches.shared.misc.debugging -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch @@ -20,8 +21,8 @@ private const val EXTENSION_CLASS_DESCRIPTOR = * Patch shared with YouTube and YT Music. */ internal fun enableDebuggingPatch( - sharedExtensionPatch: Patch<*>, - settingsPatch: Patch<*>, + sharedExtensionPatch: Patch, + settingsPatch: Patch, vararg compatibleWithPackages: Pair>, hookStringFeatureFlag: Boolean, preferenceScreen: BasePreferenceScreen.Screen, @@ -36,7 +37,7 @@ internal fun enableDebuggingPatch( settingsPatch, addResourcesPatch, resourcePatch { - execute { + apply { copyResources( "settings", ResourceGroup( @@ -49,15 +50,14 @@ internal fun enableDebuggingPatch( "revanced_settings_arrow_left_double.xml", "revanced_settings_arrow_left_one.xml", "revanced_settings_arrow_right_double.xml", - "revanced_settings_arrow_right_one.xml" - ) + "revanced_settings_arrow_right_one.xml", + ), ) } - } + }, ) - - execute { + apply { addResources("shared", "misc.debugging.enableDebuggingPatch") val preferences = setOf( @@ -68,18 +68,18 @@ internal fun enableDebuggingPatch( NonInteractivePreference( "revanced_debug_export_logs_to_clipboard", tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference", - selectable = true + selectable = true, ), NonInteractivePreference( "revanced_debug_logs_clear_buffer", tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference", - selectable = true + selectable = true, ), NonInteractivePreference( "revanced_debug_feature_flags_manager", tag = "app.revanced.extension.shared.settings.preference.FeatureFlagsManagerPreference", - selectable = true - ) + selectable = true, + ), ) preferenceScreen.addPreferences( @@ -87,13 +87,11 @@ internal fun enableDebuggingPatch( key = "revanced_debug_screen", sorting = Sorting.UNSORTED, preferences = preferences, - ) + ), ) // Hook the methods that look up if a feature flag is active. - experimentalBooleanFeatureFlagFingerprint.match( - experimentalFeatureFlagParentFingerprint.originalClassDef - ).method.apply { + experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalBooleanFeatureFlagMethod().apply { findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index -> val register = getInstruction(index).registerA @@ -102,30 +100,26 @@ internal fun enableDebuggingPatch( """ invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z move-result v$register - """ + """, ) } } - experimentalDoubleFeatureFlagFingerprint.match( - experimentalFeatureFlagParentFingerprint.originalClassDef - ).method.apply { + experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalDoubleFeatureFlagMethod().apply { val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE) addInstructions( insertIndex, """ - move-result-wide v0 # Also clobbers v1 (p0) since result is wide. + move-result-wide v0 # Also clobbers v1 (p0) since result is wide. invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D move-result-wide v0 return-wide v0 - """ + """, ) } - experimentalLongFeatureFlagFingerprint.match( - experimentalFeatureFlagParentFingerprint.originalClassDef - ).method.apply { + experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalLongFeatureFlagMethod().apply { val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE) addInstructions( @@ -135,24 +129,24 @@ internal fun enableDebuggingPatch( invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J move-result-wide v0 return-wide v0 - """ + """, ) } - if (hookStringFeatureFlag) experimentalStringFeatureFlagFingerprint.match( - experimentalFeatureFlagParentFingerprint.originalClassDef - ).method.apply { - val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT) + if (hookStringFeatureFlag) { + experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalStringFeatureFlagMethod().apply { + val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT) - addInstructions( - insertIndex, - """ - move-result-object v0 - invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 - """ - ) + addInstructions( + insertIndex, + """ + move-result-object v0 + invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String; + move-result-object v0 + return-object v0 + """, + ) + } } // There exists other experimental accessor methods for byte[] diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/Fingerprints.kt index 6f183dd087..39611369a5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/debugging/Fingerprints.kt @@ -1,35 +1,46 @@ package app.revanced.patches.shared.misc.debugging -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.accessFlags +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val experimentalFeatureFlagParentFingerprint = fingerprint { +internal val BytecodePatchContext.experimentalFeatureFlagParentMethod by gettingFirstImmutableMethodDeclaratively( + "Unable to parse proto typed experiment flag: " +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("L") - parameters("L", "J", "[B") - strings("Unable to parse proto typed experiment flag: ") + returnType("L") + parameterTypes("L", "J", "[B") } -internal val experimentalBooleanFeatureFlagFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getExperimentalBooleanFeatureFlagMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("L", "J", "Z") + returnType("Z") + parameterTypes("L", "J", "Z") } -internal val experimentalDoubleFeatureFlagFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getExperimentalDoubleFeatureFlagMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("D") - parameters("J", "D") + returnType("D") + parameterTypes("J", "D") } -internal val experimentalLongFeatureFlagFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getExperimentalLongFeatureFlagMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("J") - parameters("J", "J") + returnType("J") + parameterTypes("J", "J") } -internal val experimentalStringFeatureFlagFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getExperimentalStringFeatureFlagMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters("J", "Ljava/lang/String;") + returnType("Ljava/lang/String;") + parameterTypes("J", "Ljava/lang/String;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt index f409ee9b1b..d355b38375 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.shared.misc.dns -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch @@ -16,19 +16,19 @@ private const val EXTENSION_CLASS_DESCRIPTOR = internal fun checkWatchHistoryDomainNameResolutionPatch( block: BytecodePatchBuilder.() -> Unit = {}, executeBlock: BytecodePatchContext.() -> Unit = {}, - mainActivityFingerprint: Fingerprint + getMainActivityMethod: BytecodePatchContext.() -> MutableMethod, ) = bytecodePatch( name = "Check watch history domain name resolution", description = "Checks if the device DNS server is preventing user watch history from being saved.", ) { block() - execute { + apply { executeBlock() addResources("shared", "misc.dns.checkWatchHistoryDomainNameResolutionPatch") - mainActivityFingerprint.method.addInstruction( + getMainActivityMethod().addInstruction( 0, "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->checkDnsResolver(Landroid/app/Activity;)V", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt index 58cc5082f1..3bff13aacc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt @@ -1,13 +1,14 @@ package app.revanced.patches.shared.misc.extension -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val revancedUtilsPatchesVersionFingerprint = fingerprint { +internal val BytecodePatchContext.getPatchesReleaseVersionMethod by gettingFirstMethodDeclaratively { + name("getPatchesReleaseVersion") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters() - custom { method, _ -> - method.name == "getPatchesReleaseVersion" && method.definingClass == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Ljava/lang/String;") + parameterTypes() } 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..6df4833213 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 @@ -1,11 +1,9 @@ package app.revanced.patches.shared.misc.extension -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.FingerprintBuilder -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.firstClassDef 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 @@ -23,7 +21,7 @@ fun sharedExtensionPatch( extensionName: String, vararg hooks: ExtensionHook, ) = bytecodePatch { - dependsOn(sharedExtensionPatch(*hooks)) + dependsOn(sharedExtensionPatch(hooks = hooks)) extendWith("extensions/$extensionName.rve") } @@ -39,78 +37,92 @@ fun sharedExtensionPatch( ) = 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.") - } + apply { + // Verify the extension class exists. + firstClassDef(EXTENSION_CLASS_DESCRIPTOR) } - finalize { - // The hooks are made in finalize to ensure that the context is hooked before any other patches. + afterDependents { + // The hooks are made in afterDependents to ensure that the context is hooked before any other patches. hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) } // Modify Utils method to include the patches release version. - revancedUtilsPatchesVersionFingerprint.method.apply { - /** - * @return The file path for the jar this classfile is contained inside. - */ - fun getCurrentJarFilePath(): String { - val className = object {}::class.java.enclosingClass.name.replace('.', '/') + ".class" - val classUrl = object {}::class.java.classLoader?.getResource(className) - if (classUrl != null) { - val urlString = classUrl.toString() + /** + * @return The file path for the jar this classfile is contained inside. + */ + fun getCurrentJarFilePath(): String { + val className = object {}::class.java.enclosingClass.name.replace('.', '/') + ".class" + val classUrl = object {}::class.java.classLoader?.getResource(className) + if (classUrl != null) { + val urlString = classUrl.toString() - if (urlString.startsWith("jar:file:")) { - val end = urlString.lastIndexOf('!') + if (urlString.startsWith("jar:file:")) { + val end = urlString.lastIndexOf('!') - return URLDecoder.decode(urlString.substring("jar:file:".length, end), "UTF-8") - } + return URLDecoder.decode(urlString.substring("jar:file:".length, end), "UTF-8") } - throw IllegalStateException("Not running from inside a JAR file.") } - - /** - * @return The value for the manifest entry, - * or "Unknown" if the entry does not exist or is blank. - */ - @Suppress("SameParameterValue") - fun getPatchesManifestEntry(attributeKey: String) = JarFile(getCurrentJarFilePath()).use { jarFile -> - jarFile.manifest.mainAttributes.entries.firstOrNull { it.key.toString() == attributeKey }?.value?.toString() - ?: "Unknown" - } - - val manifestValue = getPatchesManifestEntry("Version") - returnEarly(manifestValue) + throw IllegalStateException("Not running from inside a JAR file.") } + + /** + * @return The value for the manifest entry, + * or "Unknown" if the entry does not exist or is blank. + */ + @Suppress("SameParameterValue") + fun getPatchesManifestEntry(attributeKey: String) = JarFile(getCurrentJarFilePath()).use { jarFile -> + jarFile.manifest.mainAttributes.entries.firstOrNull { it.key.toString() == attributeKey }?.value?.toString() + ?: "Unknown" + } + + val manifestValue = getPatchesManifestEntry("Version") + + getPatchesReleaseVersionMethod.returnEarly(manifestValue) } } class ExtensionHook internal constructor( - internal val fingerprint: Fingerprint, - private val insertIndexResolver: BytecodePatchContext.(Method) -> Int, - private val contextRegisterResolver: BytecodePatchContext.(Method) -> String, + private val getInsertIndex: Method.() -> Int, + private val getContextRegister: Method.() -> String, + private val build: context(MutableList) MutablePredicateList.() -> Unit, ) { - context(BytecodePatchContext) + context(context: BytecodePatchContext) operator fun invoke(extensionClassDescriptor: String) { - val insertIndex = insertIndexResolver(fingerprint.method) - val contextRegister = contextRegisterResolver(fingerprint.method) + val method = context.firstMethodDeclaratively(build = build) + val insertIndex = method.getInsertIndex() + val contextRegister = method.getContextRegister() - fingerprint.method.addInstruction( + method.addInstruction( insertIndex, "invoke-static/range { $contextRegister .. $contextRegister }, " + - "$extensionClassDescriptor->setContext(Landroid/content/Context;)V", + "$extensionClassDescriptor->setContext(Landroid/content/Context;)V", ) } } fun extensionHook( - insertIndexResolver: BytecodePatchContext.(Method) -> Int = { 0 }, - contextRegisterResolver: BytecodePatchContext.(Method) -> String = { "p0" }, - fingerprint: Fingerprint, -) = ExtensionHook(fingerprint, insertIndexResolver, contextRegisterResolver) + getInsertIndex: Method.() -> Int = { 0 }, + getContextRegister: Method.() -> String = { "p0" }, + build: context(MutableList) MutablePredicateList.() -> Unit, +) = ExtensionHook(getInsertIndex, getContextRegister, build) + +/** + * 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 'starts with' or 'ends with' string for the activity such as `/MainActivity;` + */ +fun activityOnCreateExtensionHook(activityClassType: String) = extensionHook { + name("onCreate") + definingClass(activityClassType) + returnType("V") + + /* + * Leaving this out (for now?), because before the refactor many apps + * which used a simpler fingerprint checking only method name and classDef name + * were refactored to use this instead, which caused their hooks to fail. + */ + // parameterTypes("Landroid/os/Bundle;") + } -fun extensionHook( - insertIndexResolver: BytecodePatchContext.(Method) -> Int = { 0 }, - contextRegisterResolver: BytecodePatchContext.(Method) -> String = { "p0" }, - fingerprintBuilderBlock: FingerprintBuilder.() -> Unit, -) = extensionHook(insertIndexResolver, contextRegisterResolver, fingerprint(block = fingerprintBuilderBlock)) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt index 80d041ada8..5dfe15c705 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt @@ -1,18 +1,19 @@ package app.revanced.patches.shared.misc.fix.verticalscroll -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -internal val canScrollVerticallyFingerprint = fingerprint { +internal val BytecodePatchContext.canScrollVerticallyMethodMatch by composingFirstMethod { + definingClass("SwipeRefreshLayout;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() + returnType("Z") + parameterTypes() opcodes( Opcode.MOVE_RESULT, Opcode.RETURN, Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT + Opcode.MOVE_RESULT, ) - custom { _, classDef -> classDef.endsWith("SwipeRefreshLayout;") } -} \ No newline at end of file +} 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..54a396d476 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 @@ -1,7 +1,7 @@ package app.revanced.patches.shared.misc.fix.verticalscroll -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -9,16 +9,18 @@ val verticalScrollPatch = bytecodePatch( description = "Fixes issues with refreshing the feed when the first component is of type EmptyComponent.", ) { - execute { - canScrollVerticallyFingerprint.method.apply { - val moveResultIndex = canScrollVerticallyFingerprint.patternMatch!!.endIndex - val moveResultRegister = getInstruction(moveResultIndex).registerA + apply { + canScrollVerticallyMethodMatch.let { + it.method.apply { + val moveResultIndex = it[-1] + 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 3548370110..8018c9bb66 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,43 +1,39 @@ package app.revanced.patches.shared.misc.gms -import app.revanced.patcher.fingerprint -import app.revanced.patches.shared.misc.gms.EXTENSION_CLASS_DESCRIPTOR +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId" - -internal val googlePlayUtilityFingerprint = fingerprint { +internal val BytecodePatchContext.googlePlayUtilityMethod by gettingFirstMethodDeclarativelyOrNull( + "This should never happen.", + "MetadataValueReader", + "com.google.android.gms", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("I") - parameters("L", "I") - strings( - "This should never happen.", - "MetadataValueReader", - "com.google.android.gms", - ) + returnType("I") + parameterTypes("L", "I") } -internal val serviceCheckFingerprint = fingerprint { +internal val BytecodePatchContext.serviceCheckMethod by gettingFirstMethodDeclaratively( + "Google Play Services not available", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("V") - parameters("L", "I") - strings("Google Play Services not available") + returnType("V") + parameterTypes("L", "I") } -internal val getGmsCoreVendorGroupIdFingerprint = fingerprint { +internal val BytecodePatchContext.getGmsCoreVendorGroupIdMethod by gettingFirstMethodDeclaratively { + name("getGmsCoreVendorGroupId") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters() - custom { method, classDef -> - method.name == "getGmsCoreVendorGroupId" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Ljava/lang/String;") + parameterTypes() } -internal val originalPackageNameExtensionFingerprint = fingerprint { +internal val BytecodePatchContext.originalPackageNameExtensionMethod by gettingFirstMethodDeclaratively { + name("getOriginalPackageName") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters() - custom { methodDef, classDef -> - methodDef.name == "getOriginalPackageName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Ljava/lang/String;") + parameterTypes() } 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 5f10d80967..1ab994b61c 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 @@ -1,10 +1,19 @@ package app.revanced.patches.shared.misc.gms -import app.revanced.patcher.Fingerprint -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.patch.* +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.extensions.string +import app.revanced.patcher.patch.BytecodePatchBuilder +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.patch.Option +import app.revanced.patcher.patch.Patch +import app.revanced.patcher.patch.ResourcePatchBuilder +import app.revanced.patcher.patch.ResourcePatchContext +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption import app.revanced.patches.all.misc.packagename.changePackageNamePatch import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName import app.revanced.patches.all.misc.resources.addResources @@ -17,10 +26,7 @@ import app.revanced.util.* import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c -import com.android.tools.smali.dexlib2.iface.reference.StringReference import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference -import com.android.tools.smali.dexlib2.util.MethodUtil import java.net.URI internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/GmsCoreSupport;" @@ -33,9 +39,9 @@ private const val PACKAGE_NAME_REGEX_PATTERN = "^[a-z]\\w*(\\.[a-z]\\w*)+\$" * * @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 primeMethodFingerprint The fingerprint of the "prime" method that needs to be patched. - * @param earlyReturnFingerprints The fingerprints of methods that need to be returned early. - * @param mainActivityOnCreateFingerprintToInsertIndex The fingerprint of the main activity onCreate method + * @param getPrimeMethod The fingerprint of the "prime" method that needs to be patched. + * @param getEarlyReturnMethods The methods that need to be returned early. + * @param getMainActivityOnCreateMethodToGetInsertIndex The main activity onCreate method * and a function to get the index to insert the GmsCore check instruction at. * @param extensionPatch The patch responsible for the extension. * @param gmsCoreSupportResourcePatchFactory The factory for the corresponding resource patch @@ -46,11 +52,11 @@ private const val PACKAGE_NAME_REGEX_PATTERN = "^[a-z]\\w*(\\.[a-z]\\w*)+\$" fun gmsCoreSupportPatch( fromPackageName: String, toPackageName: String, - primeMethodFingerprint: Fingerprint? = null, - earlyReturnFingerprints: Set = setOf(), - mainActivityOnCreateFingerprintToInsertIndex: Pair Int>, - extensionPatch: Patch<*>, - gmsCoreSupportResourcePatchFactory: (gmsCoreVendorGroupIdOption: Option) -> Patch<*>, + getPrimeMethod: (BytecodePatchContext.() -> MutableMethod)? = null, + getEarlyReturnMethods: Set MutableMethod> = setOf(), + getMainActivityOnCreateMethodToGetInsertIndex: Pair MutableMethod, BytecodePatchContext.() -> Int>, + extensionPatch: Patch, + gmsCoreSupportResourcePatchFactory: (gmsCoreVendorGroupIdOption: Option) -> Patch, executeBlock: BytecodePatchContext.() -> Unit = {}, block: BytecodePatchBuilder.() -> Unit = {}, ) = bytecodePatch( @@ -73,41 +79,9 @@ fun gmsCoreSupportPatch( extensionPatch, ) - execute { + apply { val gmsCoreVendorGroupId = gmsCoreVendorGroupIdOption.value!! - fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach { - val mutableClass by lazy { - proxy(it).mutableClass - } - - it.methods.forEach classLoop@{ method -> - val implementation = method.implementation ?: return@classLoop - - val mutableMethod by lazy { - mutableClass.methods.first { MethodUtil.methodSignaturesMatch(it, method) } - } - - implementation.instructions.forEachIndexed insnLoop@{ index, instruction -> - val string = - ((instruction as? Instruction21c)?.reference as? StringReference)?.string - ?: return@insnLoop - - // Apply transformation. - val transformedString = transform(string) ?: return@insnLoop - - mutableMethod.replaceInstruction( - index, - BuilderInstruction21c( - Opcode.CONST_STRING, - instruction.registerA, - ImmutableStringReference(transformedString), - ), - ) - } - } - } - fun transformPackages(string: String): String? = when (string) { "com.google", "com.google.android.gms", @@ -156,43 +130,51 @@ fun gmsCoreSupportPatch( ::transformContentUrlAuthority, ) - transformStringReferences transform@{ string -> - transformations.forEach { transform -> - transform(string)?.let { transformedString -> return@transform transformedString } - } + forEachInstructionAsSequence({ _, _, instruction, index -> + val string = instruction.string ?: return@forEachInstructionAsSequence null - return@transform null + val transformedString = transformations.firstNotNullOfOrNull { it(string) } + ?: return@forEachInstructionAsSequence null + + index to transformedString + }) { method, (index, transformedString) -> + val register = method.getInstruction(index).registerA + + method.replaceInstruction( + index, + BuilderInstruction21c( + Opcode.CONST_STRING, + register, + ImmutableStringReference(transformedString), + ), + ) } // Specific method that needs to be patched. - if (primeMethodFingerprint?.methodOrNull != null) { - val primeMethod = primeMethodFingerprint.method + if (getPrimeMethod != null) getPrimeMethod().apply { + val index = indexOfFirstInstruction { string == fromPackageName } + val register = getInstruction(index).registerA - val index = primeMethod.indexOfFirstInstruction { - getReference()?.string == fromPackageName - } - val register = primeMethod.getInstruction(index).registerA - - primeMethod.replaceInstruction( + replaceInstruction( index, "const-string v$register, \"$packageName\"", ) } + // Return these methods early to prevent the app from crashing. - earlyReturnFingerprints.forEach { it.method.returnEarly() } - serviceCheckFingerprint.method.returnEarly() + getEarlyReturnMethods.forEach { it().returnEarly() } + serviceCheckMethod.returnEarly() // Google Play Utility is not present in all apps, so we need to check if it's present. - googlePlayUtilityFingerprint.methodOrNull?.returnEarly(0) + googlePlayUtilityMethod?.returnEarly(0) // Set original and patched package names for extension to use. - originalPackageNameExtensionFingerprint.method.returnEarly(fromPackageName) + originalPackageNameExtensionMethod.returnEarly(fromPackageName) - // Verify GmsCore is installed and whitelisted for power optimizations and background usage. - - mainActivityOnCreateFingerprintToInsertIndex.let { (fingerprint, getInsertIndex) -> - fingerprint.method.addInstruction( + // Run GmsCore presence, correct installation and update checks in the main activity. + getMainActivityOnCreateMethodToGetInsertIndex.let { (getMethod, getInsertIndex) -> + getMethod().addInstruction( getInsertIndex(), "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" + "checkGmsCore(Landroid/app/Activity;)V", @@ -200,7 +182,7 @@ fun gmsCoreSupportPatch( } // Change the vendor of GmsCore in the extension. - getGmsCoreVendorGroupIdFingerprint.method.returnEarly(gmsCoreVendorGroupId) + getGmsCoreVendorGroupIdMethod.returnEarly(gmsCoreVendorGroupId) executeBlock() } @@ -234,7 +216,7 @@ fun gmsCoreSupportResourcePatch( val gmsCoreVendorGroupId = gmsCoreVendorGroupIdOption.value!! - execute { + apply { addResources("shared", "misc.gms.gmsCoreSupportResourcePatch") val toPackageName = setOrGetFallbackPackageName(toPackageName) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatchBuilder.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatchBuilder.kt index 8b6bc4dc2e..98269bd356 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatchBuilder.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatchBuilder.kt @@ -1,13 +1,29 @@ package app.revanced.patches.shared.misc.hex +import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.rawResourcePatch import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.math.max -fun hexPatch(ignoreMissingTargetFiles: Boolean = false, block: HexPatchBuilder.() -> Unit) = - hexPatch(ignoreMissingTargetFiles, fun(): Set = HexPatchBuilder().apply(block)) +fun hexPatch(ignoreMissingTargetFiles: Boolean = false, block: HexPatchBuilder.() -> Unit): Patch { + return rawResourcePatch { + apply { + HexPatchBuilder().apply(block).groupBy { it.targetFilePath } + .forEach { (targetFilePath, replacements) -> + val targetFile = get(targetFilePath, true) + if (ignoreMissingTargetFiles && !targetFile.exists()) return@forEach + + // TODO: Use a file channel to read and write the file instead of reading the whole file into memory, + // in order to reduce memory usage. + val targetFileBytes = targetFile.readBytes() + replacements.forEach { it.replacePattern(targetFileBytes) } + targetFile.writeBytes(targetFileBytes) + } + } + } +} @Suppress("JavaDefaultMethodsNotOverriddenByDelegation") class HexPatchBuilder internal constructor( @@ -35,28 +51,6 @@ class HexPatchBuilder internal constructor( } } -// The replacements being passed using a function is intended. -// Previously the replacements were a property of the patch. Getter were being delegated to that property. -// This late evaluation was being leveraged in app.revanced.patches.all.misc.hex.HexPatch. -// Without the function, the replacements would be evaluated at the time of patch creation. -// This isn't possible because the delegated property is not accessible at that time. -@Deprecated("Use the hexPatch function with the builder parameter instead.") -fun hexPatch(ignoreMissingTargetFiles: Boolean = false, replacementsSupplier: () -> Set) = - rawResourcePatch { - execute { - replacementsSupplier().groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) -> - val targetFile = get(targetFilePath, true) - if (ignoreMissingTargetFiles && !targetFile.exists()) return@forEach - - // TODO: Use a file channel to read and write the file instead of reading the whole file into memory, - // in order to reduce memory usage. - val targetFileBytes = targetFile.readBytes() - replacements.forEach { it.replacePattern(targetFileBytes) } - targetFile.writeBytes(targetFileBytes) - } - } - } - /** * Represents a pattern to search for and its replacement pattern in a file. * @@ -71,17 +65,6 @@ class Replacement( ) { val replacementBytesPadded = replacementBytes + ByteArray(bytes.size - replacementBytes.size) - @Deprecated("Use the constructor with ByteArray parameters instead.") - constructor( - pattern: String, - replacementPattern: String, - targetFilePath: String, - ) : this( - byteArrayOf(pattern), - byteArrayOf(replacementPattern), - targetFilePath - ) - /** * Replaces the [bytes] with the [replacementBytes] in the [targetFileBytes]. * diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt index d405a5853d..1c3292bdea 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt @@ -1,58 +1,64 @@ package app.revanced.patches.shared.misc.litho.filter -import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val lithoFilterFingerprint = fingerprint { +internal val BytecodePatchContext.lithoFilterInitMethod by gettingFirstMethodDeclaratively { + definingClass("/LithoFilterPatch;") accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - custom { _, classDef -> - classDef.endsWith("/LithoFilterPatch;") +} + +internal val BytecodePatchContext.protobufBufferReferenceMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("[B") + + var methodDefiningClass = "" + custom { + methodDefiningClass = definingClass + true } + + instructions( + allOf( + Opcode.IGET_OBJECT(), + field { definingClass == methodDefiningClass && type == "Lcom/google/android/libraries/elements/adl/UpbMessage;" }, + ), + method { definingClass == "Lcom/google/android/libraries/elements/adl/UpbMessage;" && name == "jniDecode" }, + ) } /** * Matches a method that use the protobuf of our component. */ -internal val protobufBufferReferenceFingerprint = fingerprint { +internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("I", "Ljava/nio/ByteBuffer;") - opcodes( - Opcode.IPUT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - Opcode.SUB_INT_2ADDR, - ) + returnType("V") + parameterTypes("I", "Ljava/nio/ByteBuffer;") + opcodes(Opcode.IPUT, Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, Opcode.SUB_INT_2ADDR) } -internal val componentContextParserFingerprint = fingerprint { - strings("Number of bits must be positive") +internal val BytecodePatchContext.componentContextParserMethodMatch by composingFirstMethod { + instructions("Number of bits must be positive"()) } -internal val emptyComponentFingerprint = fingerprint { +internal val BytecodePatchContext.emptyComponentMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) - parameters() - strings("EmptyComponent") - custom { _, classDef -> - classDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 - } + parameterTypes() + instructions("EmptyComponent"()) + custom { immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 } } -internal val componentCreateFingerprint = fingerprint { - strings( - "Element missing correct type extension", - "Element missing type" - ) -} +internal val BytecodePatchContext.componentCreateMethod by gettingFirstMethod( + "Element missing correct type extension", + "Element missing type", +) -internal val lithoThreadExecutorFingerprint = fingerprint { +internal val BytecodePatchContext.lithoThreadExecutorMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("I", "I", "I") - custom { method, classDef -> - classDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" && - method.containsLiteralInstruction(1L) // 1L = default thread timeout. - } + parameterTypes("I", "I", "I") + instructions(1L()) // 1L = default thread timeout. + custom { immutableClassDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt index ee91383b0e..fcc830c95c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt @@ -2,24 +2,23 @@ package app.revanced.patches.shared.misc.litho.filter -import app.revanced.patcher.Fingerprint -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.com.android.tools.smali.dexlib2.iface.value.MutableEncodedValue.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstructions +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.firstImmutableClassDef +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.shared.misc.extension.sharedExtensionPatch import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findFreeRegister -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.util.findFieldFromToString import com.android.tools.smali.dexlib2.AccessFlags 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.FieldReference +import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue /** * Used to add a hook point to the extension stub. @@ -32,20 +31,25 @@ lateinit var addLithoFilter: (String) -> Unit */ private var filterCount = 0 -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/litho/LithoFilterPatch;" +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/shared/patches/litho/LithoFilterPatch;" /** * A patch that allows to filter Litho components based on their identifier or path. * * @param componentCreateInsertionIndex The index to insert the filtering code in the component create method. - * @param conversionContextFingerprintToString The fingerprint of the conversion context to string method. + * @param insertProtobufHook This method injects a setProtoBuffer call in the protobuf decoding logic. + * @param getConversionContextToStringMethod The getter of the conversion context toString method. + * @param getExtractIdentifierFromBuffer Whether to extract the identifier from the protobuf buffer. * @param executeBlock The additional execution block of the patch. * @param block The additional block to build the patch. */ internal fun lithoFilterPatch( componentCreateInsertionIndex: Method.() -> Int, - conversionContextFingerprintToString: Fingerprint, + insertProtobufHook: BytecodePatchContext.() -> Unit, executeBlock: BytecodePatchContext.() -> Unit = {}, + getConversionContextToStringMethod: BytecodePatchContext.() -> Method, + getExtractIdentifierFromBuffer: () -> Boolean = { false }, block: BytecodePatchBuilder.() -> Unit = {}, ) = bytecodePatch( description = "Hooks the method which parses the bytes into a ComponentContext to filter components.", @@ -90,31 +94,40 @@ internal fun lithoFilterPatch( * } * } */ - execute { + apply { // Remove dummy filter from extenion static field // and add the filters included during patching. - lithoFilterFingerprint.method.apply { - removeInstructions(2, 4) // Remove dummy filter. + lithoFilterInitMethod.apply { + // Remove the array initialization with the dummy filter. + removeInstructions(6) + addInstructions( + 0, + "new-array v1, v1, [Lapp/revanced/extension/shared/patches/litho/Filter;" + ) + + // Fill the array with the filters added during patching. addLithoFilter = { classDescriptor -> addInstructions( - 2, + 1, """ - new-instance v1, $classDescriptor - invoke-direct { v1 }, $classDescriptor->()V + new-instance v0, $classDescriptor + invoke-direct { v0 }, $classDescriptor->()V const/16 v2, ${filterCount++} - aput-object v1, v0, v2 + aput-object v0, v1, v2 """, ) } } - // Add an interceptor to steal the protobuf of our component. - protobufBufferReferenceFingerprint.method.addInstruction( - 0, - "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", - ) + // Tell the extension whether to extract the identifier from the buffer. + if (getExtractIdentifierFromBuffer()) { + lithoFilterInitMethod.classDef.fields.first { it.name == "EXTRACT_IDENTIFIER_FROM_BUFFER" } + .initialValue = ImmutableBooleanEncodedValue.forBoolean(true).toMutable() + } + // Add an interceptor to steal the protobuf of our component. + insertProtobufHook() // Hook the method that parses bytes into a ComponentContext. // Allow the method to run to completion, and override the @@ -122,38 +135,29 @@ internal fun lithoFilterPatch( // It is important to allow the original code to always run to completion, // otherwise high memory usage and poor app performance can occur. + val conversionContextToStringMethod = getConversionContextToStringMethod() + // 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 - ) { - // Our instruction reads a String from a field of the ConversionContext class. - val reference = getReference() - reference?.definingClass == conversionContextFingerprintToString.originalClassDef.type - && reference.type == "Ljava/lang/String;" - } + val conversionContextIdentifierField = conversionContextToStringMethod + .findFieldFromToString("identifierProperty=") - it.method.getInstruction(index).getReference()!! - } - - val conversionContextPathBuilderField = conversionContextFingerprintToString.originalClassDef + val conversionContextPathBuilderField = conversionContextToStringMethod.immutableClassDef .fields.single { field -> field.type == "Ljava/lang/StringBuilder;" } // Find class and methods to create an empty component. - val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single { + val builderMethodDescriptor = emptyComponentMethod.immutableClassDef.methods.single { // The only static method in the class. method -> AccessFlags.STATIC.isSet(method.accessFlags) } - val emptyComponentField = classBy { + val emptyComponentField = firstImmutableClassDef { // Only one field that matches. - it.type == builderMethodDescriptor.returnType - }!!.immutableClass.fields.single() + type == builderMethodDescriptor.returnType + }.fields.single() // Match all component creations methods - componentCreateFingerprint.method.apply { + componentCreateMethod.apply { val insertIndex = componentCreateInsertionIndex() val freeRegister = findFreeRegister(insertIndex) val identifierRegister = findFreeRegister(insertIndex, freeRegister) @@ -163,7 +167,11 @@ internal fun lithoFilterPatch( insertIndex, """ move-object/from16 v$freeRegister, p2 # ConversionContext parameter - check-cast v$freeRegister, ${conversionContextFingerprintToString.originalClassDef.type} # Check we got the actual ConversionContext + + # In YT 20.41 the field is the abstract superclass. + # Check it's the actual ConversionContext just in case. + instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef.type} + if-eqz v$identifierRegister, :unfiltered # Get identifier and path from ConversionContext iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField @@ -183,28 +191,29 @@ internal fun lithoFilterPatch( :unfiltered nop - """ + """, ) } - // TODO: Check if needed in music + // TODO: Check if needed in music. // Change Litho thread executor to 1 thread to fix layout issue in unpatched YouTube. - lithoThreadExecutorFingerprint.method.addInstructions( + lithoThreadExecutorMethod.addInstructions( 0, """ invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorCorePoolSize(I)I move-result p1 invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorMaxThreads(I)I move-result p2 - """ + """, ) executeBlock() } - finalize { - // Save the number of filters added. - lithoFilterFingerprint.method.replaceInstruction(0, "const/16 v0, $filterCount") + afterDependents { + // Set the array size to the actual filter count of the array + // initialized at the beginning of the patch. + lithoFilterInitMethod.addInstructions(0, "const/16 v1, $filterCount") } block() 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..7c528e1a56 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,88 @@ package app.revanced.patches.shared.misc.mapping +import app.revanced.patcher.IndexedMatcherPredicate +import app.revanced.patcher.extensions.wideLiteral import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.resourcePatch +import com.android.tools.smali.dexlib2.iface.instruction.Instruction 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 +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"), + ; -val resourceMappingPatch = resourcePatch { - val resourceMappings = Collections.synchronizedList(mutableListOf()) + operator fun invoke(name: String): IndexedMatcherPredicate { + val id = get(name) - execute { - val threadCount = Runtime.getRuntime().availableProcessors() - val threadPoolExecutor = Executors.newFixedThreadPool(threadCount) + return { _, _, _ -> wideLiteral == id } + } - // Save the file in memory to concurrently read from it. - val resourceXmlFile = get("res/values/public.xml").readBytes() + /** + * @return A resource id of the given resource type and name. + * @throws PatchException if the resource is not found. + */ + operator fun get(name: String) = resourceMappings[value + name]?.id + ?: throw PatchException("Could not find resource type: $this name: $name") - for (threadIndex in 0 until threadCount) { - threadPoolExecutor.execute thread@{ - document(resourceXmlFile.inputStream()).use { document -> + companion object { + private val VALUE_MAP: Map = entries.associateBy { it.value } - val resources = document.documentElement.childNodes - val resourcesLength = resources.length - val jobSize = resourcesLength / threadCount - - 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 nameAttribute = node.getAttribute("name") - val typeAttribute = node.getAttribute("type") - - if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) continue - - val id = node.getAttribute("id").substring(2).toLong(16) - - resourceMappings.add(ResourceElement(typeAttribute, nameAttribute, id)) - } - } - } - } - - threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS) - - app.revanced.patches.shared.misc.mapping.resourceMappings = resourceMappings + fun fromValue(value: String) = VALUE_MAP[value] + ?: throw IllegalArgumentException("Unknown resource type: $value") } } -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(val type: ResourceType, val name: String, val id: Long) -data class ResourceElement internal constructor(val type: String, val name: String, val id: Long) +private lateinit var resourceMappings: MutableMap + +val resourceMappingPatch = resourcePatch { + apply { + // Use a stream of the file, since no modifications are done + // and using a File parameter causes the file to be re-written when closed. + document(get("res/values/public.xml").inputStream()).use { document -> + val resources = document.documentElement.childNodes + val resourcesLength = resources.length + resourceMappings = HashMap(2 * resourcesLength) + + for (i in 0 until resourcesLength) { + val node = resources.item(i) as? Element ?: continue + if (node.nodeName != "public") continue + + val nameAttribute = node.getAttribute("name") + if (nameAttribute.startsWith("APKTOOL")) continue + + val typeAttribute = node.getAttribute("type") + val id = node.getAttribute("id").substring(2).toLong(16) + + val type = ResourceType.fromValue(typeAttribute) + + resourceMappings[type.value + nameAttribute] = ResourceElement(type, nameAttribute, id) + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/DisableLicenseCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/DisableLicenseCheckPatch.kt index 6efbd8c1fa..02790ebee4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/DisableLicenseCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/DisableLicenseCheckPatch.kt @@ -1,27 +1,27 @@ package app.revanced.patches.shared.misc.pairip.license -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly import java.util.logging.Logger @Suppress("unused") -val disableLicenseCheckPatch = bytecodePatch( +val disablePairipLicenseCheckPatch = bytecodePatch( name = "Disable Pairip license check", description = "Disables Play Integrity API (Pairip) client-side license check.", - use = false + use = false, ) { - execute { - if (processLicenseResponseFingerprint.methodOrNull == null || validateLicenseResponseFingerprint.methodOrNull == null) { - return@execute Logger.getLogger(this::class.java.name) + apply { + if (processLicenseResponseMethod == null || validateLicenseResponseMethod == null) { + return@apply Logger.getLogger(this::class.java.name) .warning("Could not find Pairip licensing check. No changes applied.") } // Set first parameter (responseCode) to 0 (success status). - processLicenseResponseFingerprint.method.addInstruction(0, "const/4 p1, 0x0") + processLicenseResponseMethod!!.addInstruction(0, "const/4 p1, 0x0") // Short-circuit the license response validation. - validateLicenseResponseFingerprint.method.returnEarly() + validateLicenseResponseMethod!!.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/Fingerprints.kt index 344fd02dab..05afbe6a0b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/pairip/license/Fingerprints.kt @@ -1,17 +1,16 @@ package app.revanced.patches.shared.misc.pairip.license -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclarativelyOrNull +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val processLicenseResponseFingerprint = fingerprint { - custom { method, classDef -> - classDef.type == "Lcom/pairip/licensecheck/LicenseClient;" && - method.name == "processResponse" - } +internal val BytecodePatchContext.processLicenseResponseMethod by gettingFirstMethodDeclarativelyOrNull { + name("processResponse") + definingClass("Lcom/pairip/licensecheck/LicenseClient;") } -internal val validateLicenseResponseFingerprint = fingerprint { - custom { method, classDef -> - classDef.type == "Lcom/pairip/licensecheck/ResponseValidator;" && - method.name == "validateResponse" - } +internal val BytecodePatchContext.validateLicenseResponseMethod by gettingFirstMethodDeclarativelyOrNull { + name("validateResponse") + definingClass("Lcom/pairip/licensecheck/ResponseValidator;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/DisableSentryTelemetry.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/DisableSentryTelemetry.kt index f361a26bb4..8faa4e81ea 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/DisableSentryTelemetry.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/DisableSentryTelemetry.kt @@ -11,7 +11,7 @@ val disableSentryTelemetryPatch = resourcePatch( description = "Disables Sentry telemetry. See https://sentry.io/for/android/ for more information.", use = false, ) { - execute { + apply { fun Element.replaceOrCreate(tagName: String, attributeName: String, attributeValue: String) { val childElements = getElementsByTagName(tagName).asSequence().filterIsInstance() val targetChild = childElements.find { childElement -> @@ -20,10 +20,12 @@ val disableSentryTelemetryPatch = resourcePatch( if (targetChild != null) { targetChild.setAttribute("android:value", attributeValue) } else { - appendChild(ownerDocument.createElement(tagName).apply { - setAttribute("android:name", attributeName) - setAttribute("android:value", attributeValue) - }) + appendChild( + ownerDocument.createElement(tagName).apply { + setAttribute("android:name", attributeName) + setAttribute("android:value", attributeValue) + }, + ) } } 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..dbed057f0b 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,40 @@ package app.revanced.patches.shared.misc.privacy -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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, +internal val BytecodePatchContext.youTubeCopyTextMethodMatch by composingFirstMethod { + returnType("V") + parameterTypes("L", "Ljava/util/Map;") + instructions( + Opcode.IGET_OBJECT(), + afterAtMost(2, "text/plain"()), + afterAtMost(2, method("newPlainText")), + afterAtMost(2, Opcode.MOVE_RESULT_OBJECT()), + afterAtMost(2, method("setPrimaryClip")), ) - strings("text/plain") } -internal val youTubeSystemShareSheetFingerprint = fingerprint { - returns("V") - parameters("L", "Ljava/util/Map;") - opcodes( - Opcode.CHECK_CAST, - Opcode.GOTO, +internal val BytecodePatchContext.youTubeSystemShareSheetMethodMatch by composingFirstMethod { + returnType("V") + parameterTypes("L", "Ljava/util/Map;") + instructions( + method("setClassName"), + afterAtMost(4, method("iterator")), + afterAtMost(15, allOf(Opcode.IGET_OBJECT(), field { type == "Ljava/lang/String;" })), + afterAtMost(15, method("putExtra")), ) - 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, +internal val BytecodePatchContext.youTubeShareSheetMethodMatch by composingFirstMethod { + returnType("V") + parameterTypes("L", "Ljava/util/Map;") + instructions( + Opcode.IGET_OBJECT(), + after(allOf(Opcode.CHECK_CAST(), type("Ljava/lang/String;"))), + after(Opcode.GOTO()), + method("putExtra"), + "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..120640ce77 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,21 +1,19 @@ 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.CompositeMatch +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.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 -import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS 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 @@ -29,16 +27,16 @@ internal fun sanitizeSharingLinksPatch( block: BytecodePatchBuilder.() -> Unit = {}, executeBlock: BytecodePatchContext.() -> Unit = {}, preferenceScreen: BasePreferenceScreen.Screen, - replaceMusicLinksWithYouTube: Boolean = false + replaceMusicLinksWithYouTube: Boolean = false, ) = bytecodePatch( - name = PATCH_NAME_SANITIZE_SHARING_LINKS, - description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS, + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from shared links.", ) { block() dependsOn(addResourcesPatch) - execute { + apply { executeBlock() addResources("shared", "misc.privacy.sanitizeSharingLinksPatch") @@ -53,42 +51,49 @@ internal fun sanitizeSharingLinksPatch( tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory", preferences = setOf( sanitizePreference, - SwitchPreference("revanced_replace_music_with_youtube") - ) + SwitchPreference("revanced_replace_music_with_youtube"), + ), ) } else { sanitizePreference - } + }, ) - fun Fingerprint.hook( - getInsertIndex: Match.PatternMatch.() -> Int, - getUrlRegister: MutableMethod.(insertIndex: Int) -> Int, - ) { - val insertIndex = patternMatch!!.getInsertIndex() - val urlRegister = method.getUrlRegister(insertIndex) + + fun CompositeMatch.hookUrlString(matchIndex: Int) { + val index = get(matchIndex) + 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 CompositeMatch.hookIntentPutExtra(matchIndex: Int) { + val index = get(matchIndex) + val urlRegister = method.getInstruction(index).registerE + + method.addInstructionsAtControlFlowLabel( + index, + """ + invoke-static { v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$urlRegister + """ + ) } + + // YouTube share sheet copy link. + youTubeCopyTextMethodMatch.hookUrlString(0) + + // YouTube share sheet other apps. + youTubeShareSheetMethodMatch.hookIntentPutExtra(3) + // Native system share sheet. - youTubeSystemShareSheetFingerprint.hook(getInsertIndex = { endIndex }) { insertIndex -> - getInstruction(insertIndex - 1).registerA - } - - youTubeCopyTextFingerprint.hook(getInsertIndex = { startIndex + 2 }) { insertIndex -> - getInstruction(insertIndex - 2).registerA - } + youTubeSystemShareSheetMethodMatch.hookIntentPutExtra(3) } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/Fingerprints.kt index cad00d8dea..bd7a941b7f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/Fingerprints.kt @@ -1,23 +1,28 @@ package app.revanced.patches.shared.misc.settings -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import app.revanced.patches.shared.misc.extension.EXTENSION_CLASS_DESCRIPTOR import com.android.tools.smali.dexlib2.AccessFlags -internal val themeLightColorResourceNameFingerprint = fingerprint { +internal val BytecodePatchContext.themeLightColorResourceNameMethod by gettingFirstMethodDeclaratively { + name("getThemeLightColorResourceName") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters() - custom { method, classDef -> - method.name == "getThemeLightColorResourceName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Ljava/lang/String;") + parameterTypes() } -internal val themeDarkColorResourceNameFingerprint = fingerprint { +internal val BytecodePatchContext.themeDarkColorResourceNameMethod by gettingFirstMethodDeclaratively { + name("getThemeDarkColorResourceName") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters() - custom { method, classDef -> - method.name == "getThemeDarkColorResourceName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Ljava/lang/String;") + parameterTypes() } 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..efcb89c14b 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,17 +17,8 @@ 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 +private var lightThemeColor: String? = null +private var darkThemeColor: String? = null /** * Sets the default theme colors used in various ReVanced specific settings menus. @@ -41,12 +31,12 @@ fun overrideThemeColors(lightThemeColorString: String?, darkThemeColorString: St } private val settingsColorPatch = bytecodePatch { - finalize { + afterDependents { if (lightThemeColor != null) { - themeLightColorResourceNameFingerprint.method.returnEarly(lightThemeColor!!) + themeLightColorResourceNameMethod.returnEarly(lightThemeColor!!) } if (darkThemeColor != null) { - themeDarkColorResourceNameFingerprint.method.returnEarly(darkThemeColor!!) + themeDarkColorResourceNameMethod.returnEarly(darkThemeColor!!) } } } @@ -58,32 +48,47 @@ private val settingsColorPatch = bytecodePatch { * File names that do not exist are ignored and not processed. * @param preferences A set of preferences to add to the ReVanced fragment. */ -fun settingsPatch ( +fun settingsPatch( rootPreferences: List>? = null, preferences: Set, ) = resourcePatch { dependsOn( addResourcesPatch, settingsColorPatch, - addBrandLicensePatch + addBrandLicensePatch, ) - execute { + apply { copyResources( "settings", - ResourceGroup("xml", "revanced_prefs.xml", "revanced_prefs_icons.xml"), - ResourceGroup("menu", "revanced_search_menu.xml"), - ResourceGroup("drawable", + 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", + ResourceGroup( + "layout", "revanced_custom_list_item_checked.xml", // Color picker. "revanced_color_dot_widget.xml", @@ -97,14 +102,14 @@ fun settingsPatch ( "revanced_preference_search_result_list.xml", "revanced_preference_search_result_regular.xml", "revanced_preference_search_result_switch.xml", - "revanced_settings_with_toolbar.xml" - ) + "revanced_settings_with_toolbar.xml", + ), ) addResources("shared", "misc.settings.settingsResourcePatch") } - finalize { + afterDependents { fun Node.addPreference(preference: BasePreference) { preference.serialize(ownerDocument) { resource -> // TODO: Currently, resources can only be added to "values", which may not be the correct place. @@ -142,20 +147,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..6de553397f 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 @@ -16,7 +16,7 @@ abstract class BasePreferenceScreen( } /** - * Finalize and insert root preference into resource patch + * Insert root preference into resource patch */ abstract fun commit(screen: PreferenceScreenPreference) @@ -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..8fb0bf2f4a 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 @@ -1,28 +1,37 @@ package app.revanced.patches.shared.misc.spoof -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference +import app.revanced.patcher.accessFlags +import app.revanced.patcher.anyField +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.custom +import app.revanced.patcher.definingClass +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.immutableClassDef +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.method +import app.revanced.patcher.name +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType 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 buildInitPlaybackRequestFingerprint = fingerprint { - returns("Lorg/chromium/net/UrlRequest\$Builder;") +internal val BytecodePatchContext.buildInitPlaybackRequestMethodMatch by composingFirstMethod("Content-Type", "Range") { + returnType($$"Lorg/chromium/net/UrlRequest$Builder;") opcodes( Opcode.MOVE_RESULT_OBJECT, Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with. ) - strings( - "Content-Type", - "Range", - ) } -internal val buildPlayerRequestURIFingerprint = fingerprint { - returns("Ljava/lang/String;") +internal val BytecodePatchContext.buildPlayerRequestURIMethodMatch by composingFirstMethod("key", "asig") { + returnType("Ljava/lang/String;") opcodes( Opcode.INVOKE_VIRTUAL, // Register holds player request URI. Opcode.MOVE_RESULT_OBJECT, @@ -31,16 +40,15 @@ internal val buildPlayerRequestURIFingerprint = fingerprint { Opcode.MONITOR_EXIT, Opcode.RETURN_OBJECT, ) - strings( - "key", - "asig", - ) } -internal val buildRequestFingerprint = fingerprint { +internal val BytecodePatchContext.buildRequestMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Lorg/chromium/net/UrlRequest") // UrlRequest; or UrlRequest$Builder; - custom { methodDef, _ -> + returnType("Lorg/chromium/net/UrlRequest") // UrlRequest; or UrlRequest$Builder; + instructions( + method("newUrlRequestBuilder"), + ) // UrlRequest; or UrlRequest$Builder; + custom { // Different targets have slightly different parameters // Earlier targets have parameters: @@ -70,31 +78,30 @@ internal val buildRequestFingerprint = fingerprint { // Lorg/chromium/net/UrlRequest$Callback; // L - val parameterTypes = methodDef.parameterTypes + val parameterTypes = parameterTypes val parameterTypesSize = parameterTypes.size (parameterTypesSize == 6 || parameterTypesSize == 7 || parameterTypesSize == 8) && - parameterTypes[1] == "Ljava/util/Map;" // URL headers. - && indexOfNewUrlRequestBuilderInstruction(methodDef) >= 0 + parameterTypes[1] == "Ljava/util/Map;" && // URL headers. + indexOfNewUrlRequestBuilderInstruction(this) >= 0 } } -internal val protobufClassParseByteBufferFingerprint = fingerprint { +internal val BytecodePatchContext.protobufClassParseByteBufferMethod by gettingFirstImmutableMethodDeclaratively { + name("parseFrom") accessFlags(AccessFlags.PROTECTED, AccessFlags.STATIC) - returns("L") - parameters("L", "Ljava/nio/ByteBuffer;") + returnType("L") + parameterTypes("L", "Ljava/nio/ByteBuffer;") opcodes( Opcode.SGET_OBJECT, Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT_OBJECT, Opcode.RETURN_OBJECT, ) - custom { method, _ -> method.name == "parseFrom" } } -internal val createStreamingDataFingerprint = fingerprint { +internal val BytecodePatchContext.createStreamingDataMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - parameters("L") + parameterTypes("L") opcodes( Opcode.IPUT_OBJECT, Opcode.IGET_OBJECT, @@ -102,17 +109,14 @@ internal val createStreamingDataFingerprint = fingerprint { Opcode.SGET_OBJECT, Opcode.IPUT_OBJECT, ) - custom { method, classDef -> - classDef.fields.any { field -> - field.name == "a" && field.type.endsWith("/StreamingDataOuterClass\$StreamingData;") - } + custom { + immutableClassDef.anyField { name == "a" && type.endsWith($$"/StreamingDataOuterClass$StreamingData;") } } } -internal val buildMediaDataSourceFingerprint = fingerprint { +internal val BytecodePatchContext.buildMediaDataSourceMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - parameters( + parameterTypes( "Landroid/net/Uri;", "J", "I", @@ -126,78 +130,72 @@ internal val buildMediaDataSourceFingerprint = fingerprint { ) } -internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L - -internal val hlsCurrentTimeFingerprint = fingerprint { +internal val BytecodePatchContext.hlsCurrentTimeMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Z", "L") - literal { - HLS_CURRENT_TIME_FEATURE_FLAG - } + parameterTypes("Z", "L") + instructions( + 45355374L(), // HLS current time feature flag. + ) } internal const val DISABLED_BY_SABR_STREAMING_URI_STRING = "DISABLED_BY_SABR_STREAMING_URI" -internal val mediaFetchEnumConstructorFingerprint = fingerprint { - returns("V") - strings( - "ENABLED", - "DISABLED_FOR_PLAYBACK", - DISABLED_BY_SABR_STREAMING_URI_STRING +internal val BytecodePatchContext.mediaFetchEnumConstructorMethodMatch by composingFirstMethod { + returnType("V") + instructions( + "ENABLED"(), + "DISABLED_FOR_PLAYBACK"(), + DISABLED_BY_SABR_STREAMING_URI_STRING(), ) } -internal val nerdsStatsVideoFormatBuilderFingerprint = fingerprint { +internal val BytecodePatchContext.nerdsStatsVideoFormatBuilderMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Ljava/lang/String;") - parameters("L") - strings("codecs=\"") + returnType("Ljava/lang/String;") + parameterTypes("L") + instructions( + "codecs=\""(), + ) } -internal val patchIncludedExtensionMethodFingerprint = fingerprint { - returns("Z") - parameters() - custom { method, classDef -> - classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded" - } +internal val BytecodePatchContext.patchIncludedExtensionMethodMethod by gettingFirstMethodDeclaratively { + name("isPatchIncluded") + definingClass(EXTENSION_CLASS_DESCRIPTOR) + returnType("Z") + parameterTypes() } // Feature flag that turns on Platypus programming language code compiled to native C++. // 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 } +internal val BytecodePatchContext.mediaFetchHotConfigMethodMatch by composingFirstMethod { + instructions(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 } +internal val BytecodePatchContext.mediaFetchHotConfigAlternativeMethodMatch by composingFirstMethod { + instructions(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 +// but its 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 } +internal val BytecodePatchContext.playbackStartDescriptorFeatureFlagMethodMatch by composingFirstMethod { + parameterTypes() + returnType("Z") + instructions(45665455L()) } internal fun indexOfNewUrlRequestBuilderInstruction(method: Method) = method.indexOfFirstInstruction { - val reference = getReference() - opcode == Opcode.INVOKE_VIRTUAL && reference?.definingClass == "Lorg/chromium/net/CronetEngine;" - && reference.name == "newUrlRequestBuilder" - && reference.parameterTypes.size == 3 - && reference.parameterTypes[0] == "Ljava/lang/String;" - && reference.parameterTypes[1] == "Lorg/chromium/net/UrlRequest\$Callback;" - && reference.parameterTypes[2] == "Ljava/util/concurrent/Executor;" + val reference = methodReference ?: return@indexOfFirstInstruction false + + opcode == Opcode.INVOKE_VIRTUAL && reference.definingClass == "Lorg/chromium/net/CronetEngine;" && + reference.name == "newUrlRequestBuilder" && + reference.parameterTypes.size == 3 && + reference.parameterTypes[0] == "Ljava/lang/String;" && + reference.parameterTypes[1] == "Lorg/chromium/net/UrlRequest\$Callback;" && + reference.parameterTypes[2] == "Ljava/util/concurrent/Executor;" } 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..23fec8ebc0 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 @@ -1,25 +1,18 @@ package app.revanced.patches.shared.misc.spoof -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.InstructionExtensions.instructions -import app.revanced.patcher.fingerprint +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.custom +import app.revanced.patcher.extensions.* +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.opcodes 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.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patcher.returnType import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.util.findFreeRegister -import app.revanced.util.findInstructionIndicesReversedOrThrow -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.insertLiteralOverride -import app.revanced.util.returnEarly +import app.revanced.util.* import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation @@ -39,7 +32,7 @@ private var buildRequestMethodUrlRegister = -1 internal fun spoofVideoStreamsPatch( extensionClassDescriptor: String, - mainActivityOnCreateFingerprint: Fingerprint, + getMainActivityOnCreateMethod: BytecodePatchContext.() -> MutableMethod, fixMediaFetchHotConfig: BytecodePatchBuilder.() -> Boolean = { false }, fixMediaFetchHotConfigAlternative: BytecodePatchBuilder.() -> Boolean = { false }, fixParsePlaybackResponseFeatureFlag: BytecodePatchBuilder.() -> Boolean = { false }, @@ -53,24 +46,26 @@ internal fun spoofVideoStreamsPatch( dependsOn(addResourcesPatch) - execute { + apply { addResources("shared", "misc.fix.playback.spoofVideoStreamsPatch") - mainActivityOnCreateFingerprint.method.addInstruction( + getMainActivityOnCreateMethod().addInstruction( 0, - "invoke-static { }, $extensionClassDescriptor->setClientOrderToUse()V" + "invoke-static { }, $extensionClassDescriptor->setClientOrderToUse()V", ) + // TODO?: Force off 45708738L ? + // region Enable extension helper method used by other patches - patchIncludedExtensionMethodFingerprint.method.returnEarly(true) + patchIncludedExtensionMethodMethod.returnEarly(true) // endregion // region Block /initplayback requests to fall back to /get_watch requests. - buildInitPlaybackRequestFingerprint.method.apply { - val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex + buildInitPlaybackRequestMethodMatch.method.apply { + val moveUriStringIndex = buildInitPlaybackRequestMethodMatch[0] val targetRegister = getInstruction(moveUriStringIndex).registerA addInstructions( @@ -78,7 +73,7 @@ internal fun spoofVideoStreamsPatch( """ invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; move-result-object v$targetRegister - """ + """, ) } @@ -86,8 +81,8 @@ internal fun spoofVideoStreamsPatch( // region Block /get_watch requests to fall back to /player requests. - buildPlayerRequestURIFingerprint.method.apply { - val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex + buildPlayerRequestURIMethodMatch.method.apply { + val invokeToStringIndex = buildPlayerRequestURIMethodMatch[0] val uriRegister = getInstruction(invokeToStringIndex).registerC addInstructions( @@ -95,7 +90,7 @@ internal fun spoofVideoStreamsPatch( """ invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; move-result-object v$uriRegister - """ + """, ) } @@ -103,10 +98,10 @@ internal fun spoofVideoStreamsPatch( // region Get replacement streams at player requests. - buildRequestFingerprint.method.apply { + buildRequestMethodMatch.method.apply { buildRequestMethod = this - val newRequestBuilderIndex = indexOfNewUrlRequestBuilderInstruction(this) + val newRequestBuilderIndex = buildRequestMethodMatch[0] buildRequestMethodUrlRegister = getInstruction(newRequestBuilderIndex).registerD val freeRegister = findFreeRegister(newRequestBuilderIndex, buildRequestMethodUrlRegister) @@ -115,7 +110,7 @@ internal fun spoofVideoStreamsPatch( """ move-object v$freeRegister, p1 invoke-static { v$buildRequestMethodUrlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V - """ + """, ) } @@ -123,10 +118,10 @@ internal fun spoofVideoStreamsPatch( // region Replace the streaming data with the replacement streams. - createStreamingDataFingerprint.method.apply { + createStreamingDataMethodMatch.method.apply { val setStreamDataMethodName = "patch_setStreamingData" - val resultMethodType = createStreamingDataFingerprint.classDef.type - val videoDetailsIndex = createStreamingDataFingerprint.patternMatch!!.endIndex + val resultMethodType = createStreamingDataMethodMatch.classDef.type + val videoDetailsIndex = createStreamingDataMethodMatch[-1] val videoDetailsRegister = getInstruction(videoDetailsIndex).registerA val videoDetailsClass = getInstruction(videoDetailsIndex).getReference()!!.type @@ -136,8 +131,8 @@ internal fun spoofVideoStreamsPatch( "$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V", ) - val protobufClass = protobufClassParseByteBufferFingerprint.method.definingClass - val setStreamingDataIndex = createStreamingDataFingerprint.patternMatch!!.startIndex + val protobufClass = protobufClassParseByteBufferMethod.definingClass + val setStreamingDataIndex = createStreamingDataMethodMatch[0] val playerProtoClass = getInstruction(setStreamingDataIndex + 1) .getReference()!!.definingClass @@ -151,7 +146,7 @@ internal fun spoofVideoStreamsPatch( ).getReference() // Use a helper method to avoid the need of picking out multiple free registers from the hooked code. - createStreamingDataFingerprint.classDef.methods.add( + createStreamingDataMethodMatch.classDef.methods.add( ImmutableMethod( resultMethodType, setStreamDataMethodName, @@ -191,9 +186,9 @@ internal fun spoofVideoStreamsPatch( :disabled return-void - """ + """, ) - } + }, ) } @@ -205,10 +200,11 @@ internal fun spoofVideoStreamsPatch( val insertIndex = indexOfNewUrlRequestBuilderInstruction(this) addInstructions( - insertIndex, """ + insertIndex, + """ invoke-static { v$buildRequestMethodUrlRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetAttRequest(Ljava/lang/String;)Ljava/lang/String; move-result-object v$buildRequestMethodUrlRegister - """ + """, ) } @@ -219,25 +215,25 @@ internal fun spoofVideoStreamsPatch( // Requesting streams intended for other platforms with a body tuned for Android could be the cause of 400 errors. // A proper fix may include modifying the request body to match the platforms expected body. - buildMediaDataSourceFingerprint.method.apply { - val targetIndex = instructions.lastIndex + buildMediaDataSourceMethod.apply { + val targetIndex = instructions.count() - 1 // Instructions are added just before the method returns, // so there's no concern of clobbering in-use registers. 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 + """, ) } @@ -245,7 +241,7 @@ internal fun spoofVideoStreamsPatch( // region Append spoof info. - nerdsStatsVideoFormatBuilderFingerprint.method.apply { + nerdsStatsVideoFormatBuilderMethod.apply { findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index -> val register = getInstruction(index).registerA @@ -254,7 +250,7 @@ internal fun spoofVideoStreamsPatch( """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->appendSpoofedClient(Ljava/lang/String;)Ljava/lang/String; move-result-object v$register - """ + """, ) } } @@ -263,9 +259,9 @@ internal fun spoofVideoStreamsPatch( // region Fix iOS livestream current time. - hlsCurrentTimeFingerprint.method.insertLiteralOverride( - HLS_CURRENT_TIME_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z" + hlsCurrentTimeMethodMatch.method.insertLiteralOverride( + hlsCurrentTimeMethodMatch[0], + "$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z", ) // endregion @@ -273,33 +269,30 @@ internal fun spoofVideoStreamsPatch( // region Disable SABR playback. // If SABR is disabled, it seems 'MediaFetchHotConfig' may no longer need an override (not confirmed). - val (mediaFetchEnumClass, sabrFieldReference) = with(mediaFetchEnumConstructorFingerprint.method) { - val stringIndex = mediaFetchEnumConstructorFingerprint.stringMatches!!.first { - it.string == DISABLED_BY_SABR_STREAMING_URI_STRING - }.index + val (mediaFetchEnumClass, sabrFieldReference) = with(mediaFetchEnumConstructorMethodMatch.method) { + val disabledBySABRStreamingUrlString = mediaFetchEnumConstructorMethodMatch[-1] val mediaFetchEnumClass = definingClass - val sabrFieldIndex = indexOfFirstInstructionOrThrow(stringIndex) { + val sabrFieldIndex = indexOfFirstInstructionOrThrow(disabledBySABRStreamingUrlString) { opcode == Opcode.SPUT_OBJECT && - getReference()?.type == mediaFetchEnumClass + getReference()?.type == mediaFetchEnumClass } Pair( mediaFetchEnumClass, - getInstruction(sabrFieldIndex).reference + getInstruction(sabrFieldIndex).reference, ) } - fingerprint { - returns(mediaFetchEnumClass) + val sabrMethod = firstMethodDeclaratively { + returnType(mediaFetchEnumClass) opcodes( Opcode.SGET_OBJECT, Opcode.RETURN_OBJECT, ) - custom { method, _ -> - !method.parameterTypes.isEmpty() - } - }.method.addInstructionsWithLabels( + custom { !parameterTypes.isEmpty() } + } + sabrMethod.addInstructionsWithLabels( 0, """ invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableSABR()Z @@ -309,7 +302,7 @@ internal fun spoofVideoStreamsPatch( return-object v0 :ignore nop - """ + """, ) // endregion @@ -317,23 +310,23 @@ 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" + mediaFetchHotConfigMethodMatch.method.insertLiteralOverride( + mediaFetchHotConfigMethodMatch[0], + "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z", ) } if (fixMediaFetchHotConfigAlternative()) { - mediaFetchHotConfigAlternativeFingerprint.method.insertLiteralOverride( - MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" + mediaFetchHotConfigAlternativeMethodMatch.method.insertLiteralOverride( + mediaFetchHotConfigAlternativeMethodMatch[0], + "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z", ) } if (fixParsePlaybackResponseFeatureFlag()) { - playbackStartDescriptorFeatureFlagFingerprint.method.insertLiteralOverride( - PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z" + playbackStartDescriptorFeatureFlagMethodMatch.method.insertLiteralOverride( + playbackStartDescriptorFeatureFlagMethodMatch[0], + "$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z", ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatch.kt index f6ca942ad3..e512578bd8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.shared.misc.spoof -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patches.all.misc.transformation.IMethodCall import app.revanced.patches.all.misc.transformation.filterMapInstruction35c import app.revanced.patches.all.misc.transformation.transformInstructionsPatch diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/string/ReplaceStringPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/string/ReplaceStringPatch.kt index 2e83a6d93c..a6ba7a12e9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/string/ReplaceStringPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/string/ReplaceStringPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.shared.misc.string -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.util.getReference diff --git a/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt index 0e81d7e592..81992f067f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt @@ -1,15 +1,18 @@ package app.revanced.patches.solidexplorer2.functionality.filesize -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.definingClass +import app.revanced.patcher.name +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val onReadyFingerprint = fingerprint { +internal val BytecodePatchContext.onReadyMethodMatch by composingFirstMethod { + name("onReady") + definingClass("Lpl/solidexplorer/plugins/texteditor/TextEditor;") opcodes( Opcode.CONST_WIDE_32, // Constant storing the 2MB limit Opcode.CMP_LONG, Opcode.IF_LEZ, ) - custom { method, _ -> - method.name == "onReady" && method.definingClass == "Lpl/solidexplorer/plugins/texteditor/TextEditor;" - } } 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..3119eb9b43 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 @@ -1,7 +1,7 @@ package app.revanced.patches.solidexplorer2.functionality.filesize -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction @@ -12,12 +12,12 @@ val removeFileSizeLimitPatch = bytecodePatch( ) { compatibleWith("pl.solidexplorer2") - execute { - onReadyFingerprint.method.apply { - val cmpIndex = onReadyFingerprint.patternMatch!!.startIndex + 1 - val cmpResultRegister = getInstruction(cmpIndex).registerA + apply { + onReadyMethodMatch.let { + val cmpIndex = it[0] + 1 + val cmpResultRegister = it.method.getInstruction(cmpIndex).registerA - replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0") + it.method.replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0") } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt index f37ebe71ee..c1879fb2fc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt @@ -1,13 +1,13 @@ package app.revanced.patches.songpal.badge -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstructions import app.revanced.patcher.patch.bytecodePatch internal const val ACTIVITY_TAB_DESCRIPTOR = "Ljp/co/sony/vim/framework/ui/yourheadphones/YhContract\$Tab;" @Suppress("unused") -val badgeTabPatch = bytecodePatch( +val removeBadgeTabPatch = bytecodePatch( name = "Remove badge tab", description = "Removes the badge tab from the activity tab.", ) { @@ -15,8 +15,8 @@ val badgeTabPatch = bytecodePatch( val arrayTabs = listOf("Log", "HealthCare") - execute { - createTabsFingerprint.method.apply { + apply { + createTabsMethod.apply { removeInstructions(0, 2) val arrayRegister = 0 diff --git a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt index c52174f2c5..c597e3c4ef 100644 --- a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt @@ -1,49 +1,32 @@ package app.revanced.patches.songpal.badge -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference // Located @ ub.i0.h#p (9.5.0) -internal val createTabsFingerprint = fingerprint { +internal val BytecodePatchContext.createTabsMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE) - returns("Ljava/util/List;") - custom { method, _ -> - method.implementation?.instructions?.any { instruction -> - if (instruction.opcode != Opcode.INVOKE_STATIC) return@any false - - val reference = (instruction as ReferenceInstruction).reference as MethodReference - - if (reference.parameterTypes.isNotEmpty()) return@any false - if (reference.definingClass != ACTIVITY_TAB_DESCRIPTOR) return@any false - if (reference.returnType != "[${ACTIVITY_TAB_DESCRIPTOR}") return@any false - true - } == true - } + returnType("Ljava/util/List;") + instructions( + method { + parameterTypes.isEmpty() && + definingClass == ACTIVITY_TAB_DESCRIPTOR && + returnType == "[${ACTIVITY_TAB_DESCRIPTOR}" + }, + ) } // Located @ com.sony.songpal.mdr.vim.activity.MdrRemoteBaseActivity.e#run (9.5.0) -internal val showNotificationFingerprint = fingerprint { +internal val BytecodePatchContext.showNotificationMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC) - returns("V") - custom { method, _ -> - method.implementation?.instructions?.any { instruction -> - if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@any false - - with(expectedReference) { - val currentReference = (instruction as ReferenceInstruction).reference as MethodReference - currentReference.let { - if (it.definingClass != definingClass) return@any false - if (it.parameterTypes != parameterTypes) return@any false - if (it.returnType != returnType) return@any false - } - } - true - } == true - } + returnType("V") + instructions(method { + definingClass == expectedReference.definingClass + && parameterTypes == expectedReference.parameterTypes + && returnType == expectedReference.returnType + }) } internal val expectedReference = ImmutableMethodReference( diff --git a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt index eae07fc918..59aa826df0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.songpal.badge -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,7 +10,7 @@ val removeNotificationBadgePatch = bytecodePatch( ) { compatibleWith("com.sony.songpal.mdr"("10.1.0")) - execute { - showNotificationFingerprint.method.addInstructions(0, "return-void") + apply { + showNotificationMethod.addInstructions(0, "return-void") } } 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..674e225e01 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 @@ -1,25 +1,24 @@ package app.revanced.patches.soundcloud.ad -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val interceptFingerprint = fingerprint { +internal val BytecodePatchContext.interceptMethodMatch by composingFirstMethod("SC-Mob-UserPlan", "Configuration") { accessFlags(AccessFlags.PUBLIC) - returns("L") - parameters("L") + returnType("L") + parameterTypes("L") opcodes( Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT + Opcode.MOVE_RESULT_OBJECT, ) - strings("SC-Mob-UserPlan", "Configuration") } -internal val userConsumerPlanConstructorFingerprint = fingerprint { +internal val BytecodePatchContext.userConsumerPlanConstructorMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - parameters( + parameterTypes( "Ljava/lang/String;", "Z", "Ljava/lang/String;", 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..8364062aae 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 @@ -1,25 +1,19 @@ package app.revanced.patches.soundcloud.ad -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.* import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.soundcloud.shared.featureConstructorFingerprint +import app.revanced.patches.soundcloud.shared.featureConstructorMethod @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.soundcloud.android"("2025.05.27-release")) - execute { + apply { // Enable a preset feature to disable audio ads by modifying the JSON server response. // This method is the constructor of a class representing a "Feature" object parsed from JSON data. // p1 is the name of the feature. // p2 is true if the feature is enabled, false otherwise. - featureConstructorFingerprint.method.apply { + featureConstructorMethod.apply { val afterCheckNotNullIndex = 2 addInstructionsWithLabels( afterCheckNotNullIndex, @@ -41,7 +35,7 @@ val hideAdsPatch = bytecodePatch( // p4 is the "consumerPlanUpsells" value, a list of plans to try to sell to the user. // p5 is the "currentConsumerPlan" value, the type of plan currently subscribed to. // p6 is the "currentConsumerPlanTitle" value, the name of the plan currently subscribed to, shown to the user. - userConsumerPlanConstructorFingerprint.method.addInstructions( + userConsumerPlanConstructorMethod.addInstructions( 0, """ const-string p1, "high_tier" @@ -54,8 +48,8 @@ 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 - interceptFingerprint.method.addInstruction( + val conditionIndex = interceptMethodMatch[-1] + 1 + interceptMethodMatch.method.addInstruction( conditionIndex, "return-object p1", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt index 80268afc7d..b49837d6c2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.soundcloud.analytics -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,8 +10,8 @@ val disableTelemetryPatch = bytecodePatch( ) { compatibleWith("com.soundcloud.android"("2025.05.27-release")) - execute { + apply { // Empty the "backend" argument to abort the initializer. - createTrackingApiFingerprint.method.addInstruction(0, "const-string p1, \"\"") + createTrackingApiMethod.addInstruction(0, "const-string p1, \"\"") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt index 2954b4d99c..19dec5923c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt @@ -1,13 +1,17 @@ package app.revanced.patches.soundcloud.analytics -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val createTrackingApiFingerprint = fingerprint { +internal val BytecodePatchContext.createTrackingApiMethod by gettingFirstMethodDeclaratively( + "backend", + "boogaloo" +) { + name("create") accessFlags(AccessFlags.PUBLIC) - returns("L") - custom { methodDef, _ -> - methodDef.name == "create" - } - strings("backend", "boogaloo") + returnType("L") } diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt index 5714f23924..7fd1114419 100644 --- a/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt @@ -1,30 +1,23 @@ package app.revanced.patches.soundcloud.offlinesync -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -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.extensions.* import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.soundcloud.shared.featureConstructorFingerprint +import app.revanced.patches.soundcloud.shared.featureConstructorMethod import app.revanced.util.getReference import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference @Suppress("unused") -val enableOfflineSync = bytecodePatch( - name = "Enable offline sync", -) { +val enableOfflineSyncPatch = bytecodePatch("Enable offline sync") { compatibleWith("com.soundcloud.android"("2025.05.27-release")) - execute { + apply { // Enable the feature to allow offline track syncing by modifying the JSON server response. // This method is the constructor of a class representing a "Feature" object parsed from JSON data. // p1 is the name of the feature. // p2 is true if the feature is enabled, false otherwise. - featureConstructorFingerprint.method.apply { + featureConstructorMethod.apply { val afterCheckNotNullIndex = 2 addInstructionsWithLabels( @@ -42,7 +35,7 @@ val enableOfflineSync = bytecodePatch( // Patch the URL builder to use the HTTPS_STREAM endpoint // instead of the offline sync endpoint to downloading the track. - downloadOperationsURLBuilderFingerprint.method.apply { + downloadOperationsURLBuilderMethod.apply { val getEndpointsEnumFieldIndex = 1 val getEndpointsEnumFieldInstruction = getInstruction(getEndpointsEnumFieldIndex) @@ -58,7 +51,7 @@ val enableOfflineSync = bytecodePatch( // The HTTPS_STREAM endpoint does not return the necessary headers for offline sync. // Mock the headers to prevent the app from crashing by setting them to empty strings. // The headers are all cosmetic and do not affect the functionality of the app. - downloadOperationsHeaderVerificationFingerprint.method.apply { + downloadOperationsHeaderVerificationMethod.apply { // The first three null checks need to be patched. instructions.asSequence().filter { it.opcode == Opcode.IF_EQZ diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt index 688fe36044..fa73735adf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt @@ -1,13 +1,14 @@ package app.revanced.patches.soundcloud.offlinesync -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val downloadOperationsURLBuilderFingerprint = fingerprint { +internal val BytecodePatchContext.downloadOperationsURLBuilderMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String") - parameters("L", "L") + returnType("Ljava/lang/String") + parameterTypes("L", "L") opcodes( Opcode.IGET_OBJECT, Opcode.SGET_OBJECT, @@ -15,15 +16,18 @@ internal val downloadOperationsURLBuilderFingerprint = fingerprint { ) } -internal val downloadOperationsHeaderVerificationFingerprint = fingerprint { +internal val BytecodePatchContext.downloadOperationsHeaderVerificationMethod by gettingFirstMethodDeclaratively( + "X-SC-Mime-Type", + "X-SC-Preset", + "X-SC-Quality", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L", "L") + returnType("V") + parameterTypes("L", "L") opcodes( Opcode.CONST_STRING, Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT, Opcode.CONST_STRING, ) - strings("X-SC-Mime-Type", "X-SC-Preset", "X-SC-Quality") } 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..4916be459a 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 @@ -1,13 +1,16 @@ package app.revanced.patches.soundcloud.shared -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val featureConstructorFingerprint = fingerprint { - returns("V") +internal val BytecodePatchContext.featureConstructorMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("Ljava/lang/String;", "Z", "Ljava/util/List;") + parameterTypes("Ljava/lang/String;", "Z", "Ljava/util/List;") opcodes( Opcode.SGET_OBJECT, Opcode.CHECK_CAST, 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/layout/theme/CustomThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt index 55325bc4d9..6b859d3e05 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt @@ -1,7 +1,8 @@ package app.revanced.patches.spotify.layout.theme -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch @@ -18,11 +19,11 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/ private val customThemeBytecodePatch = bytecodePatch { dependsOn(sharedExtensionPatch) - execute { - val colorSpaceUtilsClassDef = colorSpaceUtilsClassFingerprint.originalClassDef + apply { + val colorSpaceUtilsClassDef = colorSpaceUtilsClassMethod.immutableClassDef // Hook a util method that converts ARGB to RGBA in the sRGB color space to replace hardcoded accent colors. - convertArgbToRgbaFingerprint.match(colorSpaceUtilsClassDef).method.apply { + colorSpaceUtilsClassDef.getConvertArgbToRgbaMethod().apply { addInstructions( 0, """ @@ -30,21 +31,21 @@ private val customThemeBytecodePatch = bytecodePatch { invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->replaceColor(I)I move-result p0 int-to-long p0, p0 - """ + """, ) } // Lottie JSON parser method. It parses the JSON Lottie animation into its own class, // including the solid color of it. - parseLottieJsonFingerprint.method.apply { + parseLottieJsonMethod.apply { val invokeParseColorIndex = indexOfFirstInstructionOrThrow { val reference = getReference() - reference?.definingClass == "Landroid/graphics/Color;" - && reference.name == "parseColor" + reference?.definingClass == "Landroid/graphics/Color;" && + reference.name == "parseColor" } val parsedColorRegister = getInstruction(invokeParseColorIndex + 1).registerA - val replaceColorDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->replaceColor(I)I" + val replaceColorDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->replaceColor(I)I" addInstructions( invokeParseColorIndex + 2, @@ -52,16 +53,16 @@ private val customThemeBytecodePatch = bytecodePatch { # Use invoke-static/range because the register number is too large. invoke-static/range { v$parsedColorRegister .. v$parsedColorRegister }, $replaceColorDescriptor move-result v$parsedColorRegister - """ + """, ) } // Lottie animated color parser. - parseAnimatedColorFingerprint.method.apply { + parseAnimatedColorMethod.apply { val invokeArgbIndex = indexOfFirstInstructionOrThrow { val reference = getReference() - reference?.definingClass == "Landroid/graphics/Color;" - && reference.name == "argb" + reference?.definingClass == "Landroid/graphics/Color;" && + reference.name == "argb" } val argbColorRegister = getInstruction(invokeArgbIndex + 1).registerA @@ -70,7 +71,7 @@ private val customThemeBytecodePatch = bytecodePatch { """ invoke-static { v$argbColorRegister }, $EXTENSION_CLASS_DESCRIPTOR->replaceColor(I)I move-result v$argbColorRegister - """ + """, ) } } @@ -87,49 +88,44 @@ val customThemePatch = resourcePatch( dependsOn(customThemeBytecodePatch) val backgroundColor by stringOption( - key = "backgroundColor", default = "@android:color/black", - title = "Primary background color", + name = "Primary background color", description = "The background color. Can be a hex color or a resource reference.", required = true, ) val overridePlayerGradientColor by booleanOption( - key = "overridePlayerGradientColor", default = false, - title = "Override player gradient color", + name = "Override player gradient color", description = "Apply primary background color to the player gradient color, which changes dynamically with the song.", required = false, ) val backgroundColorSecondary by stringOption( - key = "backgroundColorSecondary", default = "#FF121212", - title = "Secondary background color", + name = "Secondary background color", description = "The secondary background color. (e.g. playlist list in home, player artist, song credits). " + "Can be a hex color or a resource reference.\",", required = true, ) val accentColor by stringOption( - key = "accentColor", default = "#FF1ED760", - title = "Accent color", + name = "Accent color", description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.", required = true, ) val accentColorPressed by stringOption( - key = "accentColorPressed", default = "#FF1ABC54", - title = "Pressed accent color", + name = "Pressed accent color", description = "The color when accented buttons are pressed, by default slightly darker than accent. " + "Can be a hex color or a resource reference.", required = true, ) - execute { + apply { document("res/values/colors.xml").use { document -> val resourcesNode = document.getElementsByTagName("resources").item(0) as Element @@ -148,37 +144,37 @@ val customThemePatch = resourcePatch( node.textContent = when (name) { // Main background color. "gray_7", - // Left sidebar background color in tablet mode. + // Left sidebar background color in tablet mode. "gray_10", - // Gradient next to user photo and "All" in home page. + // Gradient next to user photo and "All" in home page. "dark_base_background_base", - // "Add account", "Settings and privacy", "View Profile" left sidebar background color. + // "Add account", "Settings and privacy", "View Profile" left sidebar background color. "dark_base_background_elevated_base", - // Song/player gradient start/end color. + // Song/player gradient start/end color. "bg_gradient_start_color", "bg_gradient_end_color", - // Login screen background color and gradient start. + // Login screen background color and gradient start. "sthlm_blk", "sthlm_blk_grad_start", - // Misc. + // Misc. "image_placeholder_color", -> backgroundColor // "About the artist" background color in song player. "gray_15", - // Track credits, merch background color in song player. + // Track credits, merch background color in song player. "track_credits_card_bg", "benefit_list_default_color", "merch_card_background", - // Playlist list background in home page. + // Playlist list background in home page. "opacity_white_10", - // "What's New" pills background. - "dark_base_background_tinted_highlight" + // "What's New" pills background. + "dark_base_background_tinted_highlight", -> backgroundColorSecondary "dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light", - "spotify_green_157" + "spotify_green_157", -> accentColor - "dark_brightaccent_background_press" + "dark_brightaccent_background_press", -> accentColorPressed else -> continue diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/Fingerprints.kt index eb3ab78391..584444b36f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/Fingerprints.kt @@ -1,28 +1,35 @@ package app.revanced.patches.spotify.layout.theme -import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction +import app.revanced.patcher.accessFlags +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.gettingFirstImmutableMethod +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import app.revanced.patcher.unorderedAllOf import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val colorSpaceUtilsClassFingerprint = fingerprint { - strings("The specified color must be encoded in an RGB color space.") // Partial string match. +internal val BytecodePatchContext.colorSpaceUtilsClassMethod by gettingFirstImmutableMethodDeclaratively { + instructions("The specified color must be encoded in an RGB color space."(String::contains)) } -internal val convertArgbToRgbaFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getConvertArgbToRgbaMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) - returns("J") - parameters("J") + returnType("J") + parameterTypes("J") } -internal val parseLottieJsonFingerprint = fingerprint { - strings("Unsupported matte type: ") -} +internal val BytecodePatchContext.parseLottieJsonMethod by gettingFirstMethod("Unsupported matte type: ") -internal val parseAnimatedColorFingerprint = fingerprint { - parameters("L", "F") - returns("Ljava/lang/Object;") - custom { method, _ -> - method.containsLiteralInstruction(255.0) && - method.containsLiteralInstruction(1.0) - } +internal val BytecodePatchContext.parseAnimatedColorMethod by gettingFirstMethodDeclaratively { + parameterTypes("L", "F") + returnType("Ljava/lang/Object;") + instructions(predicates = unorderedAllOf(255.0.toRawBits()(), 1.0.toRawBits()())) } 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/check/CheckEnvironmentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/check/CheckEnvironmentPatch.kt index 81d42ce664..d5321a855b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/check/CheckEnvironmentPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/check/CheckEnvironmentPatch.kt @@ -1,11 +1,11 @@ package app.revanced.patches.spotify.misc.check import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch -import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch +import app.revanced.patches.spotify.shared.mainActivityOnCreateMethod internal val checkEnvironmentPatch = checkEnvironmentPatch( - mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + getMainActivityOnCreateMethod = { mainActivityOnCreateMethod }, extensionPatch = sharedExtensionPatch, "com.spotify.music", ) 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/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Fingerprints.kt index 4f1c48e83d..065fdfa427 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Fingerprints.kt @@ -1,7 +1,11 @@ package app.revanced.patches.spotify.misc.extension -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val loadOrbitLibraryFingerprint = fingerprint { - strings("orbit_library_load", "orbit-jni-spotify") +internal val BytecodePatchContext.loadOrbitLibraryMethodMatch by composingFirstMethod { + instructions( + "orbit_library_load"(), + "orbit-jni-spotify"(), + ) } 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..60726251d5 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 @@ -1,26 +1,40 @@ package app.revanced.patches.spotify.misc.extension -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.string +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook import app.revanced.patches.shared.misc.extension.extensionHook -import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint import app.revanced.util.getReference 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 = activityOnCreateExtensionHook( + "Lcom/spotify/music/SpotifyMainActivity;" +) + +private var contextReferenceIndex = -1 internal val loadOrbitLibraryHook = extensionHook( - insertIndexResolver = { - loadOrbitLibraryFingerprint.stringMatches!!.last().index + getInsertIndex = { + indexOfFirstInstruction { + "orbit-jni-spotify" in (string ?: return@indexOfFirstInstruction false) + } }, - contextRegisterResolver = { method -> - val contextReferenceIndex = method.indexOfFirstInstruction { + getContextRegister = { + contextReferenceIndex = indexOfFirstInstruction { getReference()?.type == "Landroid/content/Context;" } - val contextRegister = method.getInstruction(contextReferenceIndex).registerA + val contextRegister = + getInstruction(contextReferenceIndex).registerA "v$contextRegister" }, - fingerprint = loadOrbitLibraryFingerprint, -) +) { + instructions( + "orbit-jni-spotify"(), + "orbit_library_load"() + ) +} 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..d1609e8ccf 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,13 +1,18 @@ package app.revanced.patches.spotify.misc.fix.login -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.gettingFirstImmutableMethod +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.literal +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val katanaProxyLoginMethodHandlerClassFingerprint = fingerprint { - strings("katana_proxy_auth") -} +internal val BytecodePatchContext.katanaProxyLoginMethodHandlerClassMethod by gettingFirstImmutableMethod("katana_proxy_auth") -internal val katanaProxyLoginMethodTryAuthorizeFingerprint = fingerprint { - strings("e2e") - literal { 0 } +context(_: BytecodePatchContext) +internal fun ClassDef.getKatanaProxyLoginMethodTryAuthorizeMethod() = firstMethodDeclaratively("e2e") { + instructions(0L()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/FixFacebookLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/FixFacebookLoginPatch.kt index f8be93ac53..d7f83a7fd5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/FixFacebookLoginPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/login/FixFacebookLoginPatch.kt @@ -1,5 +1,6 @@ package app.revanced.patches.spotify.misc.fix.login +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @@ -7,23 +8,19 @@ import app.revanced.util.returnEarly val fixFacebookLoginPatch = bytecodePatch( name = "Fix Facebook login", description = - "Fix logging in with Facebook when the app is patched by always opening the login in a web browser window.", + "Fix logging in with Facebook when the app is patched by always opening the login in a web browser window.", ) { compatibleWith("com.spotify.music") - execute { + apply { // The Facebook SDK tries to handle the login using the Facebook app in case it is installed. // However, the Facebook app does signature checks with the app that is requesting the authentication, // which ends up making the Facebook server reject with an invalid key hash for the app signature. // Override the Facebook SDK to always handle the login using the web browser, which does not perform // signature checks. - - val katanaProxyLoginMethodHandlerClass = katanaProxyLoginMethodHandlerClassFingerprint.originalClassDef // Always return 0 (no Intent was launched) as the result of trying to authorize with the Facebook app to // make the login fallback to a web browser window. - katanaProxyLoginMethodTryAuthorizeFingerprint - .match(katanaProxyLoginMethodHandlerClass) - .method - .returnEarly(0) + katanaProxyLoginMethodHandlerClassMethod.immutableClassDef + .getKatanaProxyLoginMethodTryAuthorizeMethod().returnEarly(0) } } 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..020399c703 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 @@ -1,12 +1,19 @@ 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.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.method +import app.revanced.patcher.parameterTypes import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.stringOption -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patcher.returnType 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 @@ -28,9 +35,8 @@ val changeLyricsProviderPatch = bytecodePatch( compatibleWith("com.spotify.music") val lyricsProviderHost by stringOption( - key = "lyricsProviderHost", default = "lyrics.natanchiodi.fr", - title = "Lyrics provider host", + name = "Lyrics provider host", description = "The domain name or IP address of a custom lyrics provider.", required = false, ) { @@ -49,7 +55,7 @@ val changeLyricsProviderPatch = bytecodePatch( InetAddress.getByName(host) } catch (_: UnknownHostException) { Logger.getLogger(this::class.java.name).warning( - "Host \"$host\" did not resolve to any domain." + "Host \"$host\" did not resolve to any domain.", ) } catch (_: Exception) { // Must ignore any kind of exception. Trying to resolve network @@ -59,9 +65,7 @@ val changeLyricsProviderPatch = bytecodePatch( true } - execute { - val httpClientBuilderMethod = httpClientBuilderFingerprint.originalMethod - + apply { // region Create a modified copy of the HTTP client builder method with the custom lyrics provider host. val patchedHttpClientBuilderMethod = with(httpClientBuilderMethod) { @@ -71,7 +75,7 @@ val changeLyricsProviderPatch = bytecodePatch( val setUrlBuilderHostIndex = indexOfFirstInstructionReversedOrThrow(invokeBuildUrlIndex) { val reference = getReference() reference?.definingClass == "Lokhttp3/HttpUrl${"$"}Builder;" && - reference.parameterTypes.firstOrNull() == "Ljava/lang/String;" + reference.parameterTypes.firstOrNull() == "Ljava/lang/String;" } val hostRegister = getInstruction(setUrlBuilderHostIndex).registerD @@ -79,11 +83,11 @@ val changeLyricsProviderPatch = bytecodePatch( name = "rv_getCustomLyricsProviderHttpClient" addInstruction( setUrlBuilderHostIndex, - "const-string v$hostRegister, \"$lyricsProviderHost\"" + "const-string v$hostRegister, \"$lyricsProviderHost\"", ) // Add the patched method to the class. - httpClientBuilderFingerprint.classDef.methods.add(this) + httpClientBuilderMethod.classDef.methods.add(this) } } @@ -91,7 +95,13 @@ 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 getLyricsHttpClientMethod = firstMethodDeclaratively { + returnType(httpClientBuilderMethod.returnType) + parameterTypes() + instructions(method { this == httpClientBuilderMethod }) + } + + getLyricsHttpClientMethod.apply { val getLyricsHttpClientIndex = indexOfFirstInstructionOrThrow { getReference() == httpClientBuilderMethod } @@ -112,9 +122,9 @@ val changeLyricsProviderPatch = bytecodePatch( patchedHttpClientBuilderMethod.definingClass, patchedHttpClientBuilderMethod.name, // Only difference from the original method. patchedHttpClientBuilderMethod.parameters, - patchedHttpClientBuilderMethod.returnType - ) - ) + patchedHttpClientBuilderMethod.returnType, + ), + ), ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/Fingerprints.kt index f55fc349f4..ee60ea884f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/lyrics/Fingerprints.kt @@ -1,21 +1,12 @@ package app.revanced.patches.spotify.misc.lyrics -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstImmutableMethod +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -internal val httpClientBuilderFingerprint = fingerprint { - strings("client == null", "scheduler == null") -} - -internal fun getLyricsHttpClientFingerprint(httpClientBuilderMethodReference: MethodReference) = - fingerprint { - returns(httpClientBuilderMethodReference.returnType) - parameters() - custom { method, _ -> - method.indexOfFirstInstruction { - getReference() == httpClientBuilderMethodReference - } >= 0 - } - } +internal val BytecodePatchContext.httpClientBuilderMethod by gettingFirstImmutableMethod("client == null", "scheduler == null") diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/Fingerprints.kt index a84aa699bf..6e4dbd7a13 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/Fingerprints.kt @@ -1,48 +1,45 @@ package app.revanced.patches.spotify.misc.privacy -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val shareCopyUrlFingerprint = fingerprint { - returns("Ljava/lang/Object;") - parameters("Ljava/lang/Object;") - strings("clipboard", "Spotify Link") - custom { method, _ -> - method.name == "invokeSuspend" - } +internal val BytecodePatchContext.shareCopyUrlMethod by gettingFirstMethodDeclarativelyOrNull( + "clipboard", + "Spotify Link", +) { + name("invokeSuspend") + returnType("Ljava/lang/Object;") + parameterTypes("Ljava/lang/Object;") } -internal val oldShareCopyUrlFingerprint = fingerprint { - returns("Ljava/lang/Object;") - parameters("Ljava/lang/Object;") - strings("clipboard", "createNewSession failed") - custom { method, _ -> - method.name == "apply" - } +internal val BytecodePatchContext.oldShareCopyUrlMethod by gettingFirstMethodDeclaratively( + "clipboard", + "createNewSession failed", +) { + name("apply") + returnType("Ljava/lang/Object;") + parameterTypes("Ljava/lang/Object;") } -internal val formatAndroidShareSheetUrlFingerprint = fingerprint { - returns("Ljava/lang/String;") - parameters("L", "Ljava/lang/String;") +internal val BytecodePatchContext.formatAndroidShareSheetUrlMethod by gettingFirstMethodDeclarativelyOrNull { + returnType("Ljava/lang/String;") + parameterTypes("L", "Ljava/lang/String;") opcodes( Opcode.GOTO, Opcode.IF_EQZ, Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT_OBJECT, - Opcode.RETURN_OBJECT + Opcode.RETURN_OBJECT, ) - literal { - '\n'.code.toLong() - } + literal { '\n'.code.toLong() } } -internal val oldFormatAndroidShareSheetUrlFingerprint = fingerprint { +internal val BytecodePatchContext.oldFormatAndroidShareSheetUrlMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC) - returns("Ljava/lang/String;") - parameters("Lcom/spotify/share/social/sharedata/ShareData;", "Ljava/lang/String;") - literal { - '\n'.code.toLong() - } + returnType("Ljava/lang/String;") + parameterTypes("Lcom/spotify/share/social/sharedata/ShareData;", "Ljava/lang/String;") + instructions('\n'.code.toLong()()) } diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt index 4e4ad474cf..e25384f160 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatch.kt @@ -1,42 +1,39 @@ package app.revanced.patches.spotify.misc.privacy -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.methodReference import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS -import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch -import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.AccessFlags 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/misc/privacy/SanitizeSharingLinksPatch;" @Suppress("unused") val sanitizeSharingLinksPatch = bytecodePatch( - name = PATCH_NAME_SANITIZE_SHARING_LINKS, - description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS, + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from shared links.", ) { compatibleWith("com.spotify.music") dependsOn(sharedExtensionPatch) - execute { + apply { val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" + - "sanitizeSharingLink(Ljava/lang/String;)Ljava/lang/String;" + "sanitizeSharingLink(Ljava/lang/String;)Ljava/lang/String;" - val copyFingerprint = if (shareCopyUrlFingerprint.originalMethodOrNull != null) { - shareCopyUrlFingerprint + val copyMethod = if (shareCopyUrlMethod != null) { + shareCopyUrlMethod } else { - oldShareCopyUrlFingerprint + oldShareCopyUrlMethod } - copyFingerprint.method.apply { + copyMethod!!.apply { val newPlainTextInvokeIndex = indexOfFirstInstructionOrThrow { - getReference()?.name == "newPlainText" + methodReference?.name == "newPlainText" } val urlRegister = getInstruction(newPlainTextInvokeIndex).registerD @@ -45,15 +42,14 @@ val sanitizeSharingLinksPatch = bytecodePatch( """ invoke-static { v$urlRegister }, $extensionMethodDescriptor move-result-object v$urlRegister - """ + """, ) } // Android native share sheet is used for all other quick share types (X, WhatsApp, etc). val shareUrlParameter: String - val shareSheetFingerprint = if (formatAndroidShareSheetUrlFingerprint.originalMethodOrNull != null) { - val methodAccessFlags = formatAndroidShareSheetUrlFingerprint.originalMethod - shareUrlParameter = if (AccessFlags.STATIC.isSet(methodAccessFlags.accessFlags)) { + val shareSheetMethod = if (formatAndroidShareSheetUrlMethod != null) { + shareUrlParameter = if (AccessFlags.STATIC.isSet(formatAndroidShareSheetUrlMethod!!.accessFlags)) { // In newer implementations the method is static, so p0 is not `this`. "p1" } else { @@ -62,18 +58,18 @@ val sanitizeSharingLinksPatch = bytecodePatch( "p2" } - formatAndroidShareSheetUrlFingerprint + formatAndroidShareSheetUrlMethod } else { shareUrlParameter = "p2" - oldFormatAndroidShareSheetUrlFingerprint + oldFormatAndroidShareSheetUrlMethod } - shareSheetFingerprint.method.addInstructions( + shareSheetMethod!!.addInstructions( 0, """ invoke-static { $shareUrlParameter }, $extensionMethodDescriptor move-result-object $shareUrlParameter - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/Fingerprints.kt index 3566512e84..1a238ba6f2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/Fingerprints.kt @@ -1,9 +1,10 @@ package app.revanced.patches.spotify.misc.widgets -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val canBindAppWidgetPermissionFingerprint = fingerprint { - strings("android.permission.BIND_APPWIDGET") +internal val BytecodePatchContext.canBindAppWidgetPermissionMethod by gettingFirstMethodDeclaratively("android.permission.BIND_APPWIDGET") { opcodes(Opcode.AND_INT_LIT8) } diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgets.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgets.kt index ad40f24e2e..1ce32fcebd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgets.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgets.kt @@ -4,16 +4,16 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val fixThirdPartyLaunchersWidgets = bytecodePatch( +val fixThirdPartyLaunchersWidgetsPatch = bytecodePatch( name = "Fix third party launchers widgets", description = "Fixes Spotify widgets not working in third party launchers, like Nova Launcher.", ) { compatibleWith("com.spotify.music") - execute { + apply { // Only system app launchers are granted the BIND_APPWIDGET permission. // Override the method that checks for it to always return true, as this permission is not actually required // for the widgets to work. - canBindAppWidgetPermissionFingerprint.method.returnEarly(true) + canBindAppWidgetPermissionMethod.returnEarly(true) } } 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/spotify/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/shared/Fingerprints.kt index 103f47750a..e314c40197 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/shared/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/shared/Fingerprints.kt @@ -1,15 +1,20 @@ package app.revanced.patches.spotify.shared -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags private const val SPOTIFY_MAIN_ACTIVITY = "Lcom/spotify/music/SpotifyMainActivity;" -internal val mainActivityOnCreateFingerprint = fingerprint { +internal val BytecodePatchContext.mainActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass(SPOTIFY_MAIN_ACTIVITY) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - method.name == "onCreate" && classDef.type == SPOTIFY_MAIN_ACTIVITY - } + returnType("V") + parameterTypes("Landroid/os/Bundle;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt index b7831e14ae..b63eba2d2a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt @@ -5,12 +5,10 @@ import app.revanced.util.childElementsSequence import app.revanced.util.getNode @Suppress("unused") -val hideOffersTabPatch = resourcePatch( - name = "Hide offers tab", -) { +val hideOffersTabPatch = resourcePatch("Hide offers tab") { compatibleWith("de.stocard.stocard") - execute { + apply { document("res/menu/bottom_navigation_menu.xml").use { document -> document.getNode("menu").apply { removeChild( diff --git a/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt index aab14bae99..2819b76896 100644 --- a/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt @@ -4,12 +4,10 @@ import app.revanced.patcher.patch.resourcePatch import app.revanced.util.getNode @Suppress("unused") -val hideStoryBubblesPatch = resourcePatch( - name = "Hide story bubbles", -) { +val hideStoryBubblesPatch = resourcePatch("Hide story bubbles") { compatibleWith("de.stocard.stocard") - execute { + apply { document("res/layout/rv_story_bubbles_list.xml").use { document -> document.getNode("androidx.recyclerview.widget.RecyclerView").apply { arrayOf( diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/distractions/HideDistractionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/distractions/HideDistractionsPatch.kt index 93788fc70d..67ca9f6f4f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/distractions/HideDistractionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/distractions/HideDistractionsPatch.kt @@ -1,14 +1,15 @@ package app.revanced.patches.strava.distractions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.com.android.tools.smali.dexlib2.iface.value.MutableBooleanEncodedValue.Companion.toMutable +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableClassDef +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.firstClassDef +import app.revanced.patcher.firstMethod import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable -import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableBooleanEncodedValue.Companion.toMutable import app.revanced.patches.strava.misc.extension.sharedExtensionPatch -import app.revanced.util.findMutableMethodOf import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue @@ -48,64 +49,57 @@ val hideDistractionsPatch = bytecodePatch( val logger = Logger.getLogger(this::class.java.name) - val options = arrayOf( - booleanOption( - key = "upselling", - title = "Upselling", + val options = mapOf( + "upselling" to booleanOption( + name = "Upselling", description = "Elements that suggest you subscribe.", default = true, required = true, ), - booleanOption( - key = "promo", - title = "Promotions", + "promo" to booleanOption( + name = "Promotions", description = "Elements that promote features, challenges, clubs, etc.", default = true, required = true, ), - booleanOption( - key = "followSuggestions", - title = "Who to Follow", + "followSuggestions" to booleanOption( + name = "Who to Follow", description = "Popular athletes, followers, people near you etc.", default = true, required = true, ), - booleanOption( - key = "challengeSuggestions", - title = "Suggested Challenges", + "challengeSuggestions" to booleanOption( + name = "Suggested Challenges", description = "Random challenges Strava wants you to join.", default = true, required = true, ), - booleanOption( - key = "joinChallenge", - title = "Join Challenge", + "joinChallenge" to booleanOption( + name = "Join Challenge", description = "Challenges your follows have joined.", default = false, required = true, ), - booleanOption( - key = "joinClub", - title = "Joined a club", + "joinClub" to booleanOption( + name = "Joined a club", description = "Clubs your follows have joined.", default = false, required = true, ), - booleanOption( - key = "activityLookback", - title = "Activity lookback", + "activityLookback" to booleanOption( + name = "Activity lookback", description = "Your activity from X years ago", default = false, required = true, ), ) - execute { + apply { // region Write option values into extension class. - val extensionClass = classBy { it.type == EXTENSION_CLASS_DESCRIPTOR }!!.mutableClass.apply { - options.forEach { option -> - staticFields.first { field -> field.name == option.key }.initialValue = + val extensionClass = firstClassDef { type == EXTENSION_CLASS_DESCRIPTOR }.apply { + options.forEach { (key, option) -> + staticFields.first { field -> field.name == key }.initialValue = ImmutableBooleanEncodedValue.forBoolean(option.value == true).toMutable() } } @@ -115,7 +109,7 @@ val hideDistractionsPatch = bytecodePatch( // region Intercept all classes' property getter calls. fun MutableMethod.cloneAndIntercept( - classDef: MutableClass, + classDef: MutableClassDef, extensionMethodName: String, extensionMethodParameterTypes: List, ) { @@ -128,7 +122,8 @@ val hideDistractionsPatch = bytecodePatch( if (extensionClass.directMethods.none { method -> MethodUtil.methodSignaturesMatch(method, extensionMethodReference) - }) { + } + ) { logger.info { "Skipped interception of $this due to missing $extensionMethodReference" } return } @@ -152,7 +147,7 @@ val hideDistractionsPatch = bytecodePatch( invoke-static $registers, $extensionMethodReference move-result-object v0 return-object v0 - """ + """, ) logger.fine { "Intercepted $this with $extensionMethodReference" } @@ -163,27 +158,25 @@ val hideDistractionsPatch = bytecodePatch( classDef.virtualMethods += this } - classes.filter { it.type.startsWith(MODULAR_FRAMEWORK_CLASS_DESCRIPTOR_PREFIX) }.forEach { classDef -> - val classDefProxy by lazy { proxy(classDef) } + classDefs.filter { it.type.startsWith(MODULAR_FRAMEWORK_CLASS_DESCRIPTOR_PREFIX) }.forEach { classDef -> + val mutableClassDef by lazy { classDefs.getOrReplaceMutable(classDef) } classDef.virtualMethods.forEach { method -> fingerprints.find { fingerprint -> method.name == "get${fingerprint.name}" && method.parameterTypes == fingerprint.parameterTypes }?.let { fingerprint -> - classDefProxy.mutableClass.let { mutableClass -> - // Upcast to the interface if this is an interface implementation. - val parameterType = classDef.interfaces.find { - classes.find { interfaceDef -> interfaceDef.type == it }?.virtualMethods?.any { interfaceMethod -> - MethodUtil.methodSignaturesMatch(interfaceMethod, method) - } == true - } ?: classDef.type + // Upcast to the interface if this is an interface implementation. + val parameterType = classDef.interfaces.find { + classDefs.find { interfaceDef -> interfaceDef.type == it }?.virtualMethods?.any { interfaceMethod -> + MethodUtil.methodSignaturesMatch(interfaceMethod, method) + } == true + } ?: classDef.type - mutableClass.findMutableMethodOf(method).cloneAndIntercept( - mutableClass, - "filter${fingerprint.name}", - listOf(parameterType) + fingerprint.parameterTypes - ) - } + mutableClassDef.firstMethod(method).cloneAndIntercept( + mutableClassDef, + "filter${fingerprint.name}", + listOf(parameterType) + fingerprint.parameterTypes, + ) } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivity.kt b/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityPatch.kt similarity index 89% rename from patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivity.kt rename to patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityPatch.kt index adc2b69baa..9d4756f455 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivity.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityPatch.kt @@ -1,11 +1,12 @@ package app.revanced.patches.strava.groupkudos -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.util.childElementsSequence import app.revanced.util.findElementByAttributeValueOrThrow import app.revanced.util.getReference @@ -34,7 +35,7 @@ private var leaveIdId = -1 private val addGiveKudosButtonToLayoutPatch = resourcePatch { fun String.toResourceId() = substring(2).toInt(16) - execute { + apply { document("res/values/public.xml").use { public -> fun Sequence.firstByName(name: String) = first { it.getAttribute("name") == name @@ -92,19 +93,19 @@ private val addGiveKudosButtonToLayoutPatch = resourcePatch { } @Suppress("unused") -val addGiveGroupKudosButtonToGroupActivity = bytecodePatch( +val addGiveGroupKudosButtonToGroupActivityPatch = bytecodePatch( name = "Add 'Give Kudos' button to 'Group Activity'", - description = "Adds a button that triggers the same action as shaking your phone would." + description = "Adds a button that triggers the same action as shaking your phone would.", ) { compatibleWith("com.strava") dependsOn(addGiveKudosButtonToLayoutPatch) - execute { - val className = initFingerprint.originalClassDef.type + apply { + val className = initMethod.immutableClassDef.type val onClickListenerClassName = "${className.substringBeforeLast(';')}\$GiveKudosOnClickListener;" - initFingerprint.method.apply { + initMethod.apply { val constLeaveIdInstruction = instructions.filterIsInstance().first { it.narrowLiteral == leaveIdId } @@ -126,11 +127,11 @@ val addGiveGroupKudosButtonToGroupActivity = bytecodePatch( new-instance v0, $onClickListenerClassName invoke-direct { v0, p0 }, $onClickListenerClassName->($className)V invoke-virtual { p3, v0 }, $buttonClassName->setOnClickListener($ON_CLICK_LISTENER_CLASS_DESCRIPTOR)V - """ + """, ) } - val actionHandlerMethod = actionHandlerFingerprint.match(initFingerprint.originalClassDef).method + val actionHandlerMethod = initMethod.immutableClassDef.getActionHandlerMethod() val constShakeToKudosStringIndex = actionHandlerMethod.instructions.indexOfFirst { it is NarrowLiteralInstruction && it.narrowLiteral == shakeToKudosStringId } @@ -145,7 +146,7 @@ val addGiveGroupKudosButtonToGroupActivity = bytecodePatch( PUBLIC.value or FINAL.value or SYNTHETIC.value, null, listOf(), - setOf() + setOf(), ) val initFieldMethod = ImmutableMethod( @@ -156,14 +157,14 @@ val addGiveGroupKudosButtonToGroupActivity = bytecodePatch( PUBLIC.value or SYNTHETIC.value or CONSTRUCTOR.value, setOf(), setOf(), - MutableMethodImplementation(2) + MutableMethodImplementation(2), ).toMutable().apply { addInstructions( """ invoke-direct {p0}, Ljava/lang/Object;->()V iput-object p1, p0, $outerThisField return-void - """ + """, ) } @@ -175,15 +176,15 @@ val addGiveGroupKudosButtonToGroupActivity = bytecodePatch( PUBLIC.value or FINAL.value, setOf(), setOf(), - MutableMethodImplementation(2) + MutableMethodImplementation(2), ).toMutable().apply { addInstructions( """ sget-object p1, ${getSingletonInstruction.reference} iget-object p0, p0, $outerThisField - invoke-virtual { p0, p1 }, ${actionHandlerFingerprint.method} + invoke-virtual { p0, p1 }, $actionHandlerMethod return-void - """ + """, ) } @@ -195,7 +196,7 @@ val addGiveGroupKudosButtonToGroupActivity = bytecodePatch( "ProGuard", // Same as source file name of other classes. listOf(), setOf(outerThisField), - setOf(initFieldMethod, onClickMethod) - ).let(classes::add) + setOf(initFieldMethod, onClickMethod), + ).let(classDefs::add) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/Fingerprints.kt index f5ce604fd2..4f8d9f09b8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/groupkudos/Fingerprints.kt @@ -1,14 +1,16 @@ package app.revanced.patches.strava.groupkudos -import app.revanced.patcher.fingerprint +import app.revanced.patcher.firstMethod +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val initFingerprint = fingerprint { - parameters("Lcom/strava/feed/view/modal/GroupTabFragment;" , "Z" , "Landroidx/fragment/app/FragmentManager;") - custom { method, _ -> - method.name == "" - } +internal val BytecodePatchContext.initMethod by gettingFirstMethodDeclaratively { + name("") + parameterTypes("Lcom/strava/feed/view/modal/GroupTabFragment;", "Z", "Landroidx/fragment/app/FragmentManager;") } -internal val actionHandlerFingerprint = fingerprint { - strings("state") -} +context(_: BytecodePatchContext) +internal fun ClassDef.getActionHandlerMethod() = firstMethod("state") diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/media/download/AddMediaDownloadPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/media/download/AddMediaDownloadPatch.kt index 01da305c9e..a3905c139a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/media/download/AddMediaDownloadPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/media/download/AddMediaDownloadPatch.kt @@ -1,15 +1,15 @@ package app.revanced.patches.strava.media.download -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.firstImmutableClassDef import app.revanced.patcher.patch.bytecodePatch -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.resourceMappingPatch -import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.strava.misc.extension.sharedExtensionPatch import app.revanced.util.getReference import app.revanced.util.writeRegister @@ -25,21 +25,21 @@ private const val MEDIA_DOWNLOAD_CLASS_DESCRIPTOR = "Lapp/revanced/extension/str @Suppress("unused") val addMediaDownloadPatch = bytecodePatch( name = "Add media download", - description = "Extends the full-screen media viewer menu with items to copy or open their URLs or download them directly." + description = "Extends the full-screen media viewer menu with items to copy or open their URLs or download them directly.", ) { compatibleWith("com.strava") dependsOn( resourceMappingPatch, - sharedExtensionPatch + sharedExtensionPatch, ) - execute { - val fragmentClass = classBy { it.endsWith("/FullscreenMediaFragment;") }!!.mutableClass + apply { + val fragmentClass = firstImmutableClassDef { endsWith("/FullscreenMediaFragment;") } // region Extend menu of `FullscreenMediaFragment` with actions. - createAndShowFragmentFingerprint.match(fragmentClass).method.apply { + fragmentClass.getCreateAndShowFragmentMethod().apply { val setTrueIndex = instructions.indexOfFirst { instruction -> instruction.opcode == Opcode.IPUT_BOOLEAN } @@ -54,17 +54,22 @@ val addMediaDownloadPatch = bytecodePatch( new-instance v$actionRegister, $ACTION_CLASS_DESCRIPTOR sget v${actionRegister + 1}, $MEDIA_DOWNLOAD_CLASS_DESCRIPTOR->$actionId:I const v${actionRegister + 2}, 0x0 - const v${actionRegister + 3}, ${resourceMappings["string", string]} - const v${actionRegister + 4}, ${resourceMappings["color", color]} - const v${actionRegister + 5}, ${resourceMappings["drawable", drawable]} + const v${actionRegister + 3}, ${ResourceType.STRING[string]} + const v${actionRegister + 4}, ${ResourceType.COLOR[color]} + const v${actionRegister + 5}, ${ResourceType.DRAWABLE[drawable]} move/from16 v${actionRegister + 6}, v${actionRegister + 4} invoke-direct/range { v$actionRegister .. v${actionRegister + 7} }, $ACTION_CLASS_DESCRIPTOR->(ILjava/lang/String;IIIILjava/io/Serializable;)V invoke-virtual { v$actionRegistrarRegister, v$actionRegister }, Lcom/strava/bottomsheet/a;->a(Lcom/strava/bottomsheet/BottomSheetItem;)V - """ + """, ) addMenuItem("ACTION_COPY_LINK", "copy_link", "core_o3", "actions_link_normal_xsmall") - addMenuItem("ACTION_OPEN_LINK", "fallback_menu_item_open_in_browser", "core_o3", "actions_link_external_normal_xsmall") + addMenuItem( + "ACTION_OPEN_LINK", + "fallback_menu_item_open_in_browser", + "core_o3", + "actions_link_external_normal_xsmall", + ) addMenuItem("ACTION_DOWNLOAD", "download", "core_o3", "actions_download_normal_xsmall") // Move media to last parameter of `Action` constructor. @@ -73,7 +78,7 @@ val addMediaDownloadPatch = bytecodePatch( } addInstruction( getMediaInstruction.location.index + 1, - "move-object/from16 v${actionRegister + 7}, v${getMediaInstruction.writeRegister}" + "move-object/from16 v${actionRegister + 7}, v${getMediaInstruction.writeRegister}", ) } @@ -81,15 +86,15 @@ val addMediaDownloadPatch = bytecodePatch( // region Handle new actions. - val actionClass = classes.first { clazz -> - clazz.type == ACTION_CLASS_DESCRIPTOR + val actionClass = firstImmutableClassDef { + type == ACTION_CLASS_DESCRIPTOR } val actionSerializableField = actionClass.instanceFields.first { field -> field.type == "Ljava/io/Serializable;" } // Handle "copy link" & "open link" & "download" actions. - handleMediaActionFingerprint.match(fragmentClass).method.apply { + fragmentClass.getHandleMediaActionMethod().apply { // Call handler if action ID < 0 (= custom). val moveInstruction = instructions.first { instruction -> instruction.opcode == Opcode.MOVE_RESULT @@ -107,7 +112,7 @@ val addMediaDownloadPatch = bytecodePatch( move-result v0 return v0 """, - ExternalLabel("move", instructions[indexAfterMoveInstruction]) + ExternalLabel("move", instructions[indexAfterMoveInstruction]), ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/media/download/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/media/download/Fingerprints.kt index 2f6566a827..03777c0c19 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/media/download/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/media/download/Fingerprints.kt @@ -1,15 +1,21 @@ package app.revanced.patches.strava.media.download -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val createAndShowFragmentFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getCreateAndShowFragmentMethod() = firstMethodDeclaratively("mediaType") { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - strings("mediaType") + returnType("V") + parameterTypes("L") } -internal val handleMediaActionFingerprint = fingerprint { - parameters("Landroid/view/View;", "Lcom/strava/bottomsheet/BottomSheetItem;") +context(_: BytecodePatchContext) +internal fun ClassDef.getHandleMediaActionMethod() = firstMethodDeclaratively { + parameterTypes("Landroid/view/View;", "Lcom/strava/bottomsheet/BottomSheetItem;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/Fingerprints.kt index 9653ff76bb..9f99e54cce 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/Fingerprints.kt @@ -1,21 +1,25 @@ package app.revanced.patches.strava.media.upload -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val getCompressionQualityFingerprint = fingerprint { - custom { method, _ -> - method.name == "getCompressionQuality" - } +context(_: BytecodePatchContext) +internal fun ClassDef.getGetCompressionQualityMethod() = firstMethodDeclaratively { + name("getCompressionQuality") + definingClass("/MediaUploadParameters;") } -internal val getMaxDurationFingerprint = fingerprint { - custom { method, _ -> - method.name == "getMaxDuration" - } +context(_: BytecodePatchContext) +internal fun ClassDef.getGetMaxDurationMethod() = firstMethodDeclaratively { + name("getMaxDuration") + definingClass("/MediaUploadParameters;") } -internal val getMaxSizeFingerprint = fingerprint { - custom { method, _ -> - method.name == "getMaxSize" - } +context(_: BytecodePatchContext) +internal fun ClassDef.getGetMaxSizeMethod() = firstMethodDeclaratively { + name("getMaxSize") + definingClass("/MediaUploadParameters;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/OverwriteMediaUploadParametersPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/OverwriteMediaUploadParametersPatch.kt index 1ae9bc18c4..98ad7751fe 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/OverwriteMediaUploadParametersPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/media/upload/OverwriteMediaUploadParametersPatch.kt @@ -1,5 +1,6 @@ package app.revanced.patches.strava.media.upload +import app.revanced.patcher.firstImmutableClassDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.intOption import app.revanced.patcher.patch.longOption @@ -13,36 +14,33 @@ val overwriteMediaUploadParametersPatch = bytecodePatch( compatibleWith("com.strava") val compressionQuality by intOption( - key = "compressionQuality", - title = "Compression quality (percent)", + name = "Compression quality (percent)", description = "This is used as the JPEG quality setting (≤ 100).", ) { it == null || it in 1..100 } val maxDuration by longOption( - key = "maxDuration", - title = "Max duration (seconds)", + name = "maxDuration", description = "The maximum length (≤ ${60 * 60}) of a video before it gets trimmed.", ) { it == null || it in 1..60 * 60 } val maxSize by intOption( - key = "maxSize", - title = "Max size (pixels)", + name = "Max size (pixels)", description = "The image gets resized so that the smaller dimension (width/height) does not exceed this value (≤ 10000).", ) { it == null || it in 1..10000 } - execute { - val mediaUploadParametersClass = classes.single { it.endsWith("/MediaUploadParameters;") } + apply { + val mediaUploadParametersClass = firstImmutableClassDef { type.endsWith("/MediaUploadParameters;") } compressionQuality?.let { compressionQuality -> - getCompressionQualityFingerprint.match(mediaUploadParametersClass).method.returnEarly(compressionQuality / 100f) + mediaUploadParametersClass.getGetCompressionQualityMethod().returnEarly(compressionQuality / 100f) } maxDuration?.let { maxDuration -> - getMaxDurationFingerprint.match(mediaUploadParametersClass).method.returnEarly(maxDuration) + mediaUploadParametersClass.getGetMaxDurationMethod().returnEarly(maxDuration) } maxSize?.let { - getMaxSizeFingerprint.match(mediaUploadParametersClass).method.returnEarly(it) + mediaUploadParametersClass.getGetMaxSizeMethod().returnEarly(it) } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/strava/misc/extension/Hooks.kt index 6c2a977694..4d85fd694e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/misc/extension/Hooks.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/misc/extension/Hooks.kt @@ -1,9 +1,10 @@ package app.revanced.patches.strava.misc.extension +import app.revanced.patcher.definingClass +import app.revanced.patcher.name import app.revanced.patches.shared.misc.extension.extensionHook internal val applicationOnCreateHook = extensionHook { - custom { method, classDef -> - method.name == "onCreate" && classDef.endsWith("/StravaApplication;") - } + name("onCreate") + definingClass("/StravaApplication;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/password/EnablePasswordLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/password/EnablePasswordLoginPatch.kt index 6b7e742350..3d24c92cdd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/password/EnablePasswordLoginPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/password/EnablePasswordLoginPatch.kt @@ -1,6 +1,5 @@ package app.revanced.patches.strava.password -import app.revanced.patcher.Fingerprint import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @@ -11,10 +10,8 @@ val enablePasswordLoginPatch = bytecodePatch( ) { compatibleWith("com.strava") - execute { - fun Fingerprint.returnTrue() = method.returnEarly(true) - - logInGetUsePasswordFingerprint.returnTrue() - emailChangeGetUsePasswordFingerprint.returnTrue() + apply { + logInGetUsePasswordMethod.returnEarly(true) + emailChangeGetUsePasswordMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/password/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/password/Fingerprints.kt index 94c88490a9..c8afb0fa0e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/password/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/password/Fingerprints.kt @@ -1,15 +1,16 @@ package app.revanced.patches.strava.password -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val logInGetUsePasswordFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getUsePassword" && classDef.endsWith("/RequestOtpLogInNetworkResponse;") - } +internal val BytecodePatchContext.logInGetUsePasswordMethod by gettingFirstMethodDeclaratively { + name("getUsePassword") + definingClass("/RequestOtpLogInNetworkResponse;") } -internal val emailChangeGetUsePasswordFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getUsePassword" && classDef.endsWith("/RequestEmailChangeWithOtpOrPasswordResponse;") - } +internal val BytecodePatchContext.emailChangeGetUsePasswordMethod by gettingFirstMethodDeclaratively { + name("getUsePassword") + definingClass("/RequestEmailChangeWithOtpOrPasswordResponse;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatch.kt index 313a98d1d3..4387be3575 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatch.kt @@ -10,8 +10,8 @@ val blockSnowplowTrackingPatch = bytecodePatch( ) { compatibleWith("com.strava") - execute { - // Keep events list empty, otherwise sent to https://c.strava.com/com.snowplowanalytics.snowplow/tp2. - insertEventFingerprint.method.returnEarly() + apply { + // Keep events list empty, otherwise sent to https://c.strava.com/com.snowplowanalytics.snowplow/tp2. + insertEventMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/privacy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/privacy/Fingerprints.kt index 196602ba04..5f2d921348 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/privacy/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/privacy/Fingerprints.kt @@ -1,9 +1,8 @@ package app.revanced.patches.strava.privacy -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext // https://github.com/snowplow/snowplow-android-tracker/blob/2.2.0/snowplow-tracker/src/main/java/com/snowplowanalytics/snowplow/internal/emitter/storage/SQLiteEventStore.java#L130 // Not the exact same code (e.g. returns void instead of long), even though the version number matches. -internal val insertEventFingerprint = fingerprint { - strings("Added event to database: %s") -} +internal val BytecodePatchContext.insertEventMethod by gettingFirstMethodDeclaratively("Added event to database: %s") diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/DisableQuickEditPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/DisableQuickEditPatch.kt index 128f86870f..f77b83b4dc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/DisableQuickEditPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/DisableQuickEditPatch.kt @@ -10,7 +10,7 @@ val disableQuickEditPatch = bytecodePatch( ) { compatibleWith("com.strava") - execute { - getHasAccessToQuickEditFingerprint.method.returnEarly() + apply { + getHasAccessToQuickEditMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/Fingerprints.kt index acd48542bc..51395ba3ad 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/quickedit/Fingerprints.kt @@ -1,10 +1,11 @@ package app.revanced.patches.strava.quickedit -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val getHasAccessToQuickEditFingerprint = fingerprint { - returns("Z") - custom { method, _ -> - method.name == "getHasAccessToQuickEdit" - } +internal val BytecodePatchContext.getHasAccessToQuickEditMethod by gettingFirstMethodDeclaratively { + name("getHasAccessToQuickEdit") + returnType("Z") } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt index 45583ce4e4..9aeefaa9a0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.strava.subscription -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val getSubscribedFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getSubscribed" && classDef.endsWith("/SubscriptionDetailResponse;") - } +internal val BytecodePatchContext.getSubscribedMethod by gettingFirstMethodDeclaratively { + name("getSubscribed") + definingClass("/SubscriptionDetailResponse;") } 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 e9fbc49a42..c09aeae689 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 @@ -4,13 +4,13 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -val unlockSubscriptionPatch = bytecodePatch( +val unlockSubscriptionFeaturesPatch = bytecodePatch( name = "Unlock subscription features", description = "Unlocks \"Routes\", \"Matched Runs\" and \"Segment Efforts\".", ) { compatibleWith("com.strava") - execute { - getSubscribedFingerprint.method.returnEarly(true) + apply { + getSubscribedMethod.returnEarly(true) } } 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 deleted file mode 100644 index 93ef5337af..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt +++ /dev/null @@ -1,22 +0,0 @@ -package app.revanced.patches.strava.upselling - -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.strava.distractions.hideDistractionsPatch - -@Suppress("unused") -@Deprecated("Superseded by \"Hide distractions\" patch", ReplaceWith("hideDistractionsPatch")) -val disableSubscriptionSuggestionsPatch = bytecodePatch( - name = "Disable subscription suggestions", -) { - compatibleWith("com.strava") - - dependsOn(hideDistractionsPatch.apply { - options["upselling"] = true - options["promo"] = false - options["followSuggestions"] = false - options["challengeSuggestions"] = false - options["joinChallenge"] = false - options["joinClub"] = false - options["activityLookback"] = false - }) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt deleted file mode 100644 index 1204a36f21..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.strava.upselling - -import app.revanced.patcher.fingerprint -import com.android.tools.smali.dexlib2.Opcode - -internal val getModulesFingerprint = fingerprint { - opcodes(Opcode.IGET_OBJECT) - custom { method, classDef -> - classDef.endsWith("/GenericLayoutEntry;") && method.name == "getModules" - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt index 0efa845fbe..8d3e49d84e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.swissid.integritycheck -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val checkIntegrityFingerprint = fingerprint { - returns("V") - parameters("Lcom/swisssign/deviceintegrity/model/DeviceIntegrityResult;") - strings("it", "result") +internal val BytecodePatchContext.checkIntegrityMethod by gettingFirstMethodDeclaratively("it", "result") { + returnType("V") + parameterTypes("Lcom/swisssign/deviceintegrity/model/DeviceIntegrityResult;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt index f8a193316d..ab4cb6b04a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.swissid.integritycheck -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch private const val RESULT_METHOD_REFERENCE = @@ -17,8 +17,8 @@ val removeGooglePlayIntegrityCheckPatch = bytecodePatch( ) { compatibleWith("com.swisssign.swissid.mobile"("5.2.9")) - execute { - checkIntegrityFingerprint.method.addInstructions( + apply { + checkIntegrityMethod.addInstructions( 0, """ iget-object p1, p0, $RESULT_METHOD_REFERENCE diff --git a/patches/src/main/kotlin/app/revanced/patches/threads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/threads/HideAdsPatch.kt index c718694806..e0c92255fe 100644 --- a/patches/src/main/kotlin/app/revanced/patches/threads/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/threads/HideAdsPatch.kt @@ -1,16 +1,14 @@ package app.revanced.patches.threads import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.meta.ads.adInjectorFingerprint +import app.revanced.patches.meta.ads.adInjectorMethod import app.revanced.util.returnEarly @Suppress("unused") -val hideAdsPatch = bytecodePatch( - name = "Hide ads", -) { +val hideAdsPatch = bytecodePatch("Hide ads") { compatibleWith("com.instagram.barcelona"("382.0.0.51.85")) - execute { - adInjectorFingerprint.method.returnEarly(false) + apply { + adInjectorMethod.returnEarly(false) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt index 4bd688de49..2f8683d9a0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt @@ -1,15 +1,16 @@ package app.revanced.patches.ticktick.misc.themeunlock -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val checkLockedThemesFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("Theme;") && method.name == "isLockedTheme" - } +internal val BytecodePatchContext.checkLockedThemesMethod by gettingFirstMethodDeclaratively { + name("isLockedTheme") + definingClass("Theme;") } -internal val setThemeFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("ThemePreviewActivity;") && method.name == "lambda\$updateUserBtn\$1" - } +internal val BytecodePatchContext.setThemeMethod by gettingFirstMethodDeclaratively { + name("lambda\$updateUserBtn\$1") + definingClass("ThemePreviewActivity;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt index c9d777904a..3a194f97ad 100644 --- a/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt @@ -1,18 +1,18 @@ package app.revanced.patches.ticktick.misc.themeunlock -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") -val unlockProPatch = bytecodePatch( +val unlockThemesPatch = bytecodePatch( name = "Unlock themes", description = "Unlocks all themes that are inaccessible until a certain level is reached.", ) { compatibleWith("com.ticktick.task") - execute { - checkLockedThemesFingerprint.method.addInstructions( + apply { + checkLockedThemesMethod.addInstructions( 0, """ const/4 v0, 0x0 @@ -20,6 +20,6 @@ val unlockProPatch = bytecodePatch( """, ) - setThemeFingerprint.method.removeInstructions(0, 10) + setThemeMethod.removeInstructions(0, 10) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt index 0f1227e4d3..57dfdbd3c3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt @@ -1,11 +1,11 @@ package app.revanced.patches.tiktok.feedfilter -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.settings.settingsPatch -import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint +import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -27,23 +27,22 @@ val feedFilterPatch = bytecodePatch( "com.zhiliaoapp.musically"("36.5.4"), ) - execute { + apply { arrayOf( - feedApiServiceLIZFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V", - followFeedFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V" + feedApiServiceLIZMethod to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V", + followFeedMethod to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V", ).forEach { (method, filterSignature) -> val returnInstruction = method.instructions.first { it.opcode == Opcode.RETURN_OBJECT } val register = (returnInstruction as OneRegisterInstruction).registerA method.addInstruction( returnInstruction.location.index, - "invoke-static { v$register }, $filterSignature" + "invoke-static { v$register }, $filterSignature", ) } - settingsStatusLoadFingerprint.method.addInstruction( + settingsStatusLoadMethod.addInstruction( 0, "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V", ) } - } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt index f85dd2d072..fd114a91bb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt @@ -1,22 +1,22 @@ package app.revanced.patches.tiktok.feedfilter -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val feedApiServiceLIZFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList" - } +internal val BytecodePatchContext.feedApiServiceLIZMethod by gettingFirstMethodDeclaratively { + name("fetchFeedList") + definingClass("/FeedApiService;") } -internal val followFeedFingerprint = fingerprint { +internal val BytecodePatchContext.followFeedMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;") - strings("getFollowFeedList") + returnType("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;") opcodes( Opcode.INVOKE_INTERFACE_RANGE, Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_INTERFACE ) + instructions("getFollowFeedList"(String::contains)) } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt index eb28683743..5a41acf760 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt @@ -1,10 +1,12 @@ package app.revanced.patches.tiktok.interaction.cleardisplay -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val onClearDisplayEventFingerprint = fingerprint { - custom { method, classDef -> - // Internally the feature is called "Clear mode". - classDef.endsWith("/ClearModePanelComponent;") && method.name == "onClearModeEvent" - } +internal val BytecodePatchContext.onClearDisplayEventMethod by gettingFirstMethodDeclaratively { + // Internally the feature is called "Clear mode". + name("onClearModeEvent") + definingClass("/ClearModePanelComponent;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt index 13c829e625..5ed3d1af49 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt @@ -1,11 +1,11 @@ package app.revanced.patches.tiktok.interaction.cleardisplay -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.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint +import app.revanced.patches.tiktok.shared.onRenderFirstFrameMethod import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -20,14 +20,14 @@ val rememberClearDisplayPatch = bytecodePatch( "com.zhiliaoapp.musically"("36.5.4"), ) - execute { - onClearDisplayEventFingerprint.method.let { + apply { + onClearDisplayEventMethod.apply { // region Hook the "Clear display" configuration save event to remember the state of clear display. - val isEnabledIndex = it.indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1 - val isEnabledRegister = it.getInstruction(isEnabledIndex - 1).registerA + val isEnabledIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1 + val isEnabledRegister = getInstruction(isEnabledIndex - 1).registerA - it.addInstructions( + addInstructions( isEnabledIndex, "invoke-static { v$isEnabledRegister }, " + "Lapp/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V", @@ -37,8 +37,8 @@ val rememberClearDisplayPatch = bytecodePatch( // region Override the "Clear display" configuration load event to load the state of clear display. - val clearDisplayEventClass = it.parameters[0].type - onRenderFirstFrameFingerprint.method.addInstructionsWithLabels( + val clearDisplayEventClass = parameters[0].type + onRenderFirstFrameMethod.addInstructionsWithLabels( 0, """ # Create a new clearDisplayEvent and post it to the EventBus (https://github.com/greenrobot/EventBus) @@ -61,7 +61,7 @@ val rememberClearDisplayPatch = bytecodePatch( invoke-direct { v0, v1, v2, v3, v4 }, $clearDisplayEventClass->(ILjava/lang/String;Ljava/lang/String;Z)V invoke-virtual { v0 }, $clearDisplayEventClass->post()Lcom/ss/android/ugc/governance/eventbus/IEvent; """, - ExternalLabel("clear_display_disabled", onRenderFirstFrameFingerprint.method.getInstruction(0)), + ExternalLabel("clear_display_disabled", onRenderFirstFrameMethod.getInstruction(0)), ) // endregion diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt index e6894b521e..3150c6a190 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt @@ -1,14 +1,10 @@ package app.revanced.patches.tiktok.interaction.downloads -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.* import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.settings.settingsPatch -import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint +import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.getReference import app.revanced.util.returnEarly @@ -33,12 +29,12 @@ val downloadsPatch = bytecodePatch( "com.zhiliaoapp.musically"("36.5.4"), ) - execute { - aclCommonShareFingerprint.method.returnEarly(0) - aclCommonShare2Fingerprint.method.returnEarly(2) + apply { + aclCommonShareMethod.returnEarly(0) + aclCommonShare2Method.returnEarly(2) // Download videos without watermark. - aclCommonShare3Fingerprint.method.addInstructionsWithLabels( + aclCommonShare3Method.addInstructionsWithLabels( 0, """ invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldRemoveWatermark()Z @@ -52,7 +48,7 @@ val downloadsPatch = bytecodePatch( ) // Change the download path patch. - downloadUriFingerprint.method.apply { + downloadUriMethod.apply { findInstructionIndicesReversedOrThrow { getReference().let { it?.definingClass == "Landroid/os/Environment;" && it.name.startsWith("DIRECTORY_") @@ -75,7 +71,7 @@ val downloadsPatch = bytecodePatch( } } - settingsStatusLoadFingerprint.method.addInstruction( + settingsStatusLoadMethod.addInstruction( 0, "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableDownload()V", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt index 160b49c158..a9d01076a2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt @@ -1,46 +1,42 @@ package app.revanced.patches.tiktok.interaction.downloads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val aclCommonShareFingerprint = fingerprint { +internal val BytecodePatchContext.aclCommonShareMethod by gettingFirstMethodDeclaratively { + name("getCode") + definingClass("/ACLCommonShare;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("I") - custom { method, classDef -> - classDef.endsWith("/ACLCommonShare;") && - method.name == "getCode" - } + returnType("I") } - -internal val aclCommonShare2Fingerprint = fingerprint { +internal val BytecodePatchContext.aclCommonShare2Method by gettingFirstMethodDeclaratively { + name("getShowType") + definingClass("/ACLCommonShare;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("I") - custom { method, classDef -> - classDef.endsWith("/ACLCommonShare;") && - method.name == "getShowType" - } + returnType("I") } - -internal val aclCommonShare3Fingerprint = fingerprint { +internal val BytecodePatchContext.aclCommonShare3Method by gettingFirstMethodDeclaratively { + name("getTranscode") + definingClass("/ACLCommonShare;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("I") - custom { method, classDef -> - classDef.endsWith("/ACLCommonShare;") && - method.name == "getTranscode" - } + returnType("I") } - -internal val downloadUriFingerprint = fingerprint { +internal val BytecodePatchContext.downloadUriMethod by gettingFirstMethodDeclaratively( + "/", + "/Camera", + "/Camera/", + "video/mp4", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Landroid/net/Uri;") - parameters( + returnType("Landroid/net/Uri;") + parameterTypes( "Landroid/content/Context;", - "Ljava/lang/String;" - ) - strings( - "/", - "/Camera", - "/Camera/", - "video/mp4" + "Ljava/lang/String;", ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt index ce372ea421..164d42fb38 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt @@ -1,11 +1,12 @@ package app.revanced.patches.tiktok.interaction.seekbar -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext -internal val setSeekBarShowTypeFingerprint = fingerprint { - strings("seekbar show type change, change to:") -} +internal val BytecodePatchContext.setSeekBarShowTypeMethod by gettingFirstMethodDeclaratively( + "seekbar show type change, change to:" +) -internal val shouldShowSeekBarFingerprint = fingerprint { - strings("can not show seekbar, state: 1, not in resume") -} +internal val BytecodePatchContext.shouldShowSeekBarMethod by gettingFirstMethodDeclaratively( + "can not show seekbar, state: 1, not in resume" +) \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt index 417095b339..7c8a0e914e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt @@ -1,7 +1,8 @@ package app.revanced.patches.tiktok.interaction.seekbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") val showSeekbarPatch = bytecodePatch( @@ -13,15 +14,9 @@ val showSeekbarPatch = bytecodePatch( "com.zhiliaoapp.musically", ) - execute { - shouldShowSeekBarFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) - setSeekBarShowTypeFingerprint.method.apply { + apply { + shouldShowSeekBarMethod.returnEarly(true) + setSeekBarShowTypeMethod.apply { val typeRegister = implementation!!.registerCount - 1 addInstructions( diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt index 221036bb96..8231957764 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt @@ -1,17 +1,16 @@ package app.revanced.patches.tiktok.interaction.speed -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val getSpeedFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/BaseListFragmentPanel;") && method.name == "onFeedSpeedSelectedEvent" - } +internal val BytecodePatchContext.getSpeedMethod by gettingFirstMethodDeclaratively { + name("onFeedSpeedSelectedEvent") + definingClass("/BaseListFragmentPanel;") } -internal val setSpeedFingerprint = fingerprint { +internal val BytecodePatchContext.setSpeedMethod by gettingFirstMethodDeclaratively("enterFrom") { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("V") - parameters("Ljava/lang/String;", "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", "F") - strings("enterFrom") + returnType("V") + parameterTypes("Ljava/lang/String;", "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", "F") } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt index d59626ee76..9ad16de080 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt @@ -1,13 +1,16 @@ package app.revanced.patches.tiktok.interaction.speed -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.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstClassDef import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.tiktok.shared.getEnterFromFingerprint -import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint +import app.revanced.patches.tiktok.shared.getEnterFromMethod +import app.revanced.patches.tiktok.shared.onRenderFirstFrameMethod import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x import com.android.tools.smali.dexlib2.iface.reference.MethodReference @@ -22,49 +25,41 @@ val playbackSpeedPatch = bytecodePatch( "com.zhiliaoapp.musically"("36.5.4"), ) - execute { - setSpeedFingerprint.let { onVideoSwiped -> - getSpeedFingerprint.method.apply { - val injectIndex = - indexOfFirstInstructionOrThrow { getReference()?.returnType == "F" } + 2 - val register = getInstruction(injectIndex - 1).registerA + apply { + getSpeedMethod.apply { + val injectIndex = + indexOfFirstInstructionOrThrow { getReference()?.returnType == "F" } + 2 + val register = getInstruction(injectIndex - 1).registerA - addInstruction( - injectIndex, - "invoke-static { v$register }," + - " Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V", - ) - } - - // By default, the playback speed will reset to 1.0 at the start of each video. - // Instead, override it with the desired playback speed. - onRenderFirstFrameFingerprint.method.addInstructions( - 0, - """ - # Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method. - const/4 v0, 0x1 - invoke-virtual { p0, v0 }, ${getEnterFromFingerprint.originalMethod} - move-result-object v0 - - # Model of current video retrieved using getCurrentAweme method. - invoke-virtual { p0 }, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme; - move-result-object v1 - - # Desired playback speed retrieved using getPlaybackSpeed method. - invoke-static { }, Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F - move-result v2 - invoke-static { v0, v1, v2 }, ${onVideoSwiped.originalMethod} - """, - ) - - // Force enable the playback speed option for all videos. - onVideoSwiped.classDef.methods.find { method -> method.returnType == "Z" }?.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, + addInstruction( + injectIndex, + "invoke-static { v$register }," + + " Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V", ) } + + // By default, the playback speed will reset to 1.0 at the start of each video. + // Instead, override it with the desired playback speed. + onRenderFirstFrameMethod.addInstructions( + 0, + """ + # Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method. + const/4 v0, 0x1 + invoke-virtual { p0, v0 }, $getEnterFromMethod + move-result-object v0 + + # Model of current video retrieved using getCurrentAweme method. + invoke-virtual { p0 }, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme; + move-result-object v1 + + # Desired playback speed retrieved using getPlaybackSpeed method. + invoke-static { }, Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F + move-result v2 + invoke-static { v0, v1, v2 }, ${setSpeedMethod.definingClass}->${setSpeedMethod.name}(Ljava/lang/String;Lcom/ss/android/ugc/aweme/feed/model/Aweme;F)V + """, + ) + + // Force enable the playback speed option for all videos. + setSpeedMethod.classDef.methods.find { method -> method.returnType == "Z" }?.returnEarly(true) } } 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..ed5f57046a 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,14 @@ package app.revanced.patches.tiktok.misc.extension +import app.revanced.patcher.definingClass +import app.revanced.patcher.name +import app.revanced.patcher.parameterTypes +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 @@ -17,21 +17,17 @@ internal val initHook = extensionHook { * This class is called from startup code titled "BPEA RunnableGuardLancet". */ internal val jatoInitHook = extensionHook( - contextRegisterResolver = { "p1" } + getContextRegister = { "p1" } ) { - parameters("Landroid/content/Context;") - custom { method, classDef -> - classDef.type == "Lcom/ss/android/ugc/aweme/legoImp/task/JatoInitTask;" && - method.name == "run" - } + name("run") + definingClass("Lcom/ss/android/ugc/aweme/legoImp/task/JatoInitTask;") + parameterTypes("Landroid/content/Context;") } internal val storeRegionInitHook = extensionHook( - contextRegisterResolver = { "p1" } + getContextRegister = { "p1" } ) { - parameters("Landroid/content/Context;") - custom { method, classDef -> - classDef.type == "Lcom/ss/android/ugc/aweme/legoImp/task/StoreRegionInitTask;" && - method.name == "run" - } + name("run") + definingClass("Lcom/ss/android/ugc/aweme/legoImp/task/StoreRegionInitTask;") + parameterTypes("Landroid/content/Context;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt index 2976abe5e5..2405b31118 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt @@ -1,29 +1,19 @@ package app.revanced.patches.tiktok.misc.login.disablerequirement -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") -val disableLoginRequirementPatch = bytecodePatch( - name = "Disable login requirement", -) { +val disableLoginRequirementPatch = bytecodePatch("Disable login requirement") { compatibleWith( "com.ss.android.ugc.trill", "com.zhiliaoapp.musically", ) - execute { + apply { listOf( - mandatoryLoginServiceFingerprint, - mandatoryLoginService2Fingerprint, - ).forEach { fingerprint -> - fingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x0 - return v0 - """, - ) - } + mandatoryLoginServiceMethod, + mandatoryLoginService2Method, + ).forEach { method -> method.returnEarly() } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt index 929ef86729..3cd6056480 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt @@ -1,17 +1,14 @@ package app.revanced.patches.tiktok.misc.login.disablerequirement -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val mandatoryLoginServiceFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/MandatoryLoginService;") && - method.name == "enableForcedLogin" - } +internal val BytecodePatchContext.mandatoryLoginServiceMethod by gettingFirstMethodDeclaratively { + name("enableForcedLogin") + definingClass("/MandatoryLoginService;") } -internal val mandatoryLoginService2Fingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/MandatoryLoginService;") && - method.name == "shouldShowForcedLogin" - } +internal val BytecodePatchContext.mandatoryLoginService2Method by gettingFirstMethodDeclaratively { + name("shouldShowForcedLogin") + definingClass("/MandatoryLoginService;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt index a40f5251f5..aede29be2e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt @@ -1,22 +1,19 @@ package app.revanced.patches.tiktok.misc.login.fixgoogle -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val googleAuthAvailableFingerprint = fingerprint { +internal val BytecodePatchContext.googleAuthAvailableMethod by gettingFirstMethodDeclaratively { + definingClass("Lcom/bytedance/lobby/google/GoogleAuth;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - custom { method, _ -> - method.definingClass == "Lcom/bytedance/lobby/google/GoogleAuth;" - } + returnType("Z") + parameterTypes() } -internal val googleOneTapAuthAvailableFingerprint = fingerprint { +internal val BytecodePatchContext.googleOneTapAuthAvailableMethod by gettingFirstMethodDeclaratively { + definingClass("Lcom/bytedance/lobby/google/GoogleOneTapAuth;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - custom { method, _ -> - method.definingClass == "Lcom/bytedance/lobby/google/GoogleOneTapAuth;" - } -} + returnType("Z") + parameterTypes() +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt index 7ab636c095..4c562eb9c3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt @@ -1,7 +1,8 @@ package app.revanced.patches.tiktok.misc.login.fixgoogle -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") val fixGoogleLoginPatch = bytecodePatch( @@ -13,18 +14,7 @@ val fixGoogleLoginPatch = bytecodePatch( "com.zhiliaoapp.musically", ) - execute { - listOf( - googleOneTapAuthAvailableFingerprint.method, - googleAuthAvailableFingerprint.method, - ).forEach { method -> - method.addInstructions( - 0, - """ - const/4 v0, 0x0 - return v0 - """, - ) - } + apply { + listOf(googleOneTapAuthAvailableMethod, googleAuthAvailableMethod).forEach(MutableMethod::returnEarly) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt index d1c4d6de68..9b1893ea9a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt @@ -1,35 +1,25 @@ package app.revanced.patches.tiktok.misc.settings -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val addSettingsEntryFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/SettingNewVersionFragment;") && - method.name == "initUnitManger" - } +internal val BytecodePatchContext.addSettingsEntryMethod by gettingFirstMethodDeclaratively { + name("initUnitManger") + definingClass("/SettingNewVersionFragment;") } -internal val adPersonalizationActivityOnCreateFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/AdPersonalizationActivity;") && - method.name == "onCreate" - } +internal val BytecodePatchContext.adPersonalizationActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("/AdPersonalizationActivity;") } -internal val settingsEntryFingerprint = fingerprint { - strings("pls pass item or extends the EventUnit") -} +internal val BytecodePatchContext.settingsEntryMethod by gettingFirstImmutableMethodDeclaratively( + "pls pass item or extends the EventUnit", +) -internal val settingsEntryInfoFingerprint = fingerprint { - strings( - "ExposeItem(title=", - ", icon=", - ) -} +internal val BytecodePatchContext.settingsEntryInfoMethod by gettingFirstImmutableMethod("ExposeItem(title=", ", icon=") -internal val settingsStatusLoadFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("Lapp/revanced/extension/tiktok/settings/SettingsStatus;") && - method.name == "load" - } +internal val BytecodePatchContext.settingsStatusLoadMethod by gettingFirstMethodDeclaratively { + name("load") + definingClass("Lapp/revanced/extension/tiktok/settings/SettingsStatus;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt index bc6cc2bffe..4e6a43b3a6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt @@ -1,16 +1,13 @@ package app.revanced.patches.tiktok.misc.settings -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.extensions.* +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.layout.branding.addBrandLicensePatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch +import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c -import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c -import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/settings/TikTokActivityHook;" @@ -26,7 +23,7 @@ val settingsPatch = bytecodePatch( "com.zhiliaoapp.musically"("36.5.4"), ) - execute { + apply { val initializeSettingsMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->initialize(" + "Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;" + @@ -38,16 +35,16 @@ val settingsPatch = bytecodePatch( "Ljava/lang/String;" + ")Ljava/lang/Object;" - fun String.toClassName(): String = substring(1, this.length - 1).replace("/", ".") + fun String.toClassName() = substring(1, this.length - 1).replace("/", ".") // Find the class name of classes which construct a settings entry - val settingsButtonClass = settingsEntryFingerprint.originalClassDef.type.toClassName() - val settingsButtonInfoClass = settingsEntryInfoFingerprint.originalClassDef.type.toClassName() + val settingsButtonClass = settingsEntryMethod.immutableClassDef.type.toClassName() + val settingsButtonInfoClass = settingsEntryInfoMethod.immutableClassDef.type.toClassName() // Create a settings entry for 'revanced settings' and add it to settings fragment - addSettingsEntryFingerprint.method.apply { - val markIndex = implementation!!.instructions.indexOfFirst { - it.opcode == Opcode.IGET_OBJECT && ((it as Instruction22c).reference as FieldReference).name == "headerUnit" + addSettingsEntryMethod.apply { + val markIndex = indexOfFirstInstruction { + opcode == Opcode.IGET_OBJECT && fieldReference?.name == "headerUnit" } val getUnitManager = getInstruction(markIndex + 2) @@ -68,24 +65,22 @@ val settingsPatch = bytecodePatch( const-string v1, "$settingsButtonInfoClass" invoke-static {v0, v1}, $createSettingsEntryMethodDescriptor move-result-object v0 - check-cast v0, ${settingsEntryFingerprint.originalClassDef.type} + check-cast v0, ${settingsEntryMethod.immutableClassDef.type} """, ) } // Initialize the settings menu once the replaced setting entry is clicked. - adPersonalizationActivityOnCreateFingerprint.method.apply { - val initializeSettingsIndex = implementation!!.instructions.indexOfFirst { - it.opcode == Opcode.INVOKE_SUPER - } + 1 + adPersonalizationActivityOnCreateMethod.apply { + val initializeSettingsIndex = indexOfFirstInstruction(Opcode.INVOKE_SUPER) + 1 - val thisRegister = getInstruction(initializeSettingsIndex - 1).registerC + val thisRegister = getInstruction(initializeSettingsIndex - 1).registerC val usableRegister = implementation!!.registerCount - parameters.size - 2 addInstructionsWithLabels( initializeSettingsIndex, """ - invoke-static {v$thisRegister}, $initializeSettingsMethodDescriptor + invoke-static { v$thisRegister }, $initializeSettingsMethodDescriptor move-result v$usableRegister if-eqz v$usableRegister, :do_not_open return-void diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt index 836be89006..012f87df73 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt @@ -1,25 +1,21 @@ package app.revanced.patches.tiktok.misc.share -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val urlShorteningFingerprint = fingerprint { +internal val BytecodePatchContext.urlShorteningMethod by gettingFirstMethodDeclaratively( + "getShortShareUrlObservab\u2026ongUrl, subBizSceneValue)" // Same Kotlin intrinsics literal on both variants. +) { + name("LIZLLL") // LIZLLL is obfuscated by ProGuard/R8, but stable across both TikTok and Musically. accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) - returns("LX/") - parameters( + returnType("LX/") + parameterTypes( "I", "Ljava/lang/String;", "Ljava/lang/String;", "Ljava/lang/String;" ) opcodes(Opcode.RETURN_OBJECT) - - // Same Kotlin intrinsics literal on both variants. - strings("getShortShareUrlObservab\u2026ongUrl, subBizSceneValue)") - - custom { method, _ -> - // LIZLLL is obfuscated by ProGuard/R8, but stable across both TikTok and Musically. - method.name == "LIZLLL" - } -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt index fd616141c7..da6e6aaacc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt @@ -1,11 +1,8 @@ package app.revanced.patches.tiktok.misc.share -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.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS -import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.util.findFreeRegister import app.revanced.util.getReference @@ -19,9 +16,9 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/share/ShareUrlSanitizer;" @Suppress("unused") -val sanitizeShareUrlsPatch = bytecodePatch( - name = PATCH_NAME_SANITIZE_SHARING_LINKS, - description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS, +val sanitizeSharingLinksPatch = bytecodePatch( + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from shared links.", ) { dependsOn(sharedExtensionPatch) @@ -30,11 +27,11 @@ val sanitizeShareUrlsPatch = bytecodePatch( "com.zhiliaoapp.musically"("36.5.4"), ) - execute { - urlShorteningFingerprint.method.apply { + apply { + urlShorteningMethod.apply { val invokeIndex = indexOfFirstInstructionOrThrow { - val ref = getReference() - ref?.name == "LIZ" && ref.definingClass.startsWith("LX/") + val reference = getReference() + reference?.name == "LIZ" && reference.definingClass.startsWith("LX/") } val moveResultIndex = indexOfFirstInstructionOrThrow(invokeIndex, Opcode.MOVE_RESULT_OBJECT) @@ -78,7 +75,7 @@ val sanitizeShareUrlsPatch = bytecodePatch( :skip_sanitization nop - """ + """, ) } } 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..73e4a427f6 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 @@ -1,20 +1,20 @@ package app.revanced.patches.tiktok.misc.spoof.sim -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.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstMethod import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.settings.settingsPatch -import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint -import app.revanced.util.findMutableMethodOf +import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Suppress("unused") -val spoofSimPatch = bytecodePatch( +val sIMSpoofPatch = bytecodePatch( name = "SIM spoof", description = "Spoofs the information which is retrieved from the SIM card.", use = false, @@ -29,7 +29,7 @@ val spoofSimPatch = bytecodePatch( "com.zhiliaoapp.musically", ) - execute { + apply { val replacements = hashMapOf( "getSimCountryIso" to "getCountryIso", "getNetworkCountryIso" to "getCountryIso", @@ -41,7 +41,7 @@ val spoofSimPatch = bytecodePatch( // Find all api call to check sim information. buildMap { - classes.forEach { classDef -> + classDefs.forEach { classDef -> classDef.methods.let { methods -> buildMap methodList@{ methods.forEach methods@{ method -> @@ -69,30 +69,28 @@ val spoofSimPatch = bytecodePatch( } } }.forEach { (classDef, methods) -> - with(proxy(classDef).mutableClass) { - methods.forEach { (method, patches) -> - with(findMutableMethodOf(method)) { - while (!patches.isEmpty()) { - val (index, replacement) = patches.removeLast() + methods.forEach { (method, patches) -> + with(classDef.firstMethod(method)) { + while (!patches.isEmpty()) { + val (index, replacement) = patches.removeLast() - val resultReg = getInstruction(index + 1).registerA + val resultReg = getInstruction(index + 1).registerA - // Patch Android API and return fake sim information. - addInstructions( - index + 2, - """ - invoke-static {v$resultReg}, Lapp/revanced/extension/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$resultReg - """, - ) - } + // Patch Android API and return fake sim information. + addInstructions( + index + 2, + """ + invoke-static {v$resultReg}, Lapp/revanced/extension/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$resultReg + """, + ) } } } } // Enable patch in settings. - settingsStatusLoadFingerprint.method.addInstruction( + settingsStatusLoadMethod.addInstruction( 0, "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableSimSpoof()V", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt index 3e98d213e5..1158982f34 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt @@ -1,13 +1,15 @@ package app.revanced.patches.tiktok.shared -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val getEnterFromFingerprint = fingerprint { - returns("Ljava/lang/String;") +internal val BytecodePatchContext.getEnterFromMethod by gettingFirstImmutableMethodDeclaratively { + definingClass("/BaseListFragmentPanel;") + returnType("Ljava/lang/String;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Z") + parameterTypes("Z") opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, @@ -17,14 +19,8 @@ internal val getEnterFromFingerprint = fingerprint { Opcode.MOVE_RESULT_OBJECT, Opcode.RETURN_OBJECT, ) - custom { methodDef, _ -> - methodDef.definingClass.endsWith("/BaseListFragmentPanel;") - } } -internal val onRenderFirstFrameFingerprint = fingerprint { - strings("method_enable_viewpager_preload_duration") - custom { _, classDef -> - classDef.endsWith("/BaseListFragmentPanel;") - } +internal val BytecodePatchContext.onRenderFirstFrameMethod by gettingFirstMethodDeclaratively("method_enable_viewpager_preload_duration") { + definingClass("/BaseListFragmentPanel;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt index 4a02c62215..2ab94e5348 100644 --- a/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt @@ -1,25 +1,16 @@ package app.revanced.patches.trakt -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val isVIPEPFingerprint = fingerprint { - custom { method, classDef -> - if (!classDef.endsWith("RemoteUser;")) return@custom false - - method.name == "isVIPEP" - } +internal val BytecodePatchContext.isVIPEPMethod by gettingFirstMethodDeclaratively { + name("isVIPEP") + definingClass("RemoteUser;") } -internal val isVIPFingerprint = fingerprint { - custom { method, classDef -> - if (!classDef.endsWith("RemoteUser;")) return@custom false - - method.name == "isVIP" - } -} - -internal val remoteUserFingerprint = fingerprint { - custom { _, classDef -> - classDef.endsWith("RemoteUser;") - } +internal val BytecodePatchContext.isVIPMethod by gettingFirstMethodDeclaratively { + name("isVIP") + definingClass("RemoteUser;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt index bf10b9f40e..294ea61c19 100644 --- a/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt @@ -1,21 +1,17 @@ package app.revanced.patches.trakt -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") -val unlockProPatch = bytecodePatch( - name = "Unlock pro", -) { +val unlockProPatch = bytecodePatch("Unlock pro") { compatibleWith("tv.trakt.trakt"("1.1.1")) - execute { - arrayOf(isVIPFingerprint, isVIPEPFingerprint).onEach { fingerprint -> - // Resolve both fingerprints on the same class. - fingerprint.match(remoteUserFingerprint.originalClassDef) - }.forEach { fingerprint -> - // Return true for both VIP check methods. - fingerprint.method.addInstructions( + apply { + // Return true for both VIP check methods. + arrayOf(isVIPMethod, isVIPEPMethod).forEach { method: MutableMethod -> + method.addInstructions( 0, """ const/4 v0, 0x1 diff --git a/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt index 12ffa15c15..8fee4cdcf7 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt @@ -1,15 +1,14 @@ package app.revanced.patches.tudortmund.lockscreen +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val brightnessFingerprint = fingerprint { +internal val BytecodePatchContext.brightnessMethod by gettingFirstMethodDeclaratively { + name("run") + definingClass("/ScreenPlugin$") accessFlags(AccessFlags.PUBLIC) - returns("V") - parameters() - custom { method, classDef -> - method.name == "run" && - method.definingClass.contains("/ScreenPlugin\$") && - classDef.fields.any { it.type == "Ljava/lang/Float;" } - } + returnType("V") + parameterTypes() + custom { immutableClassDef.anyField { type == "Ljava/lang/Float;" } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt index c4ab245ff6..8ab356f0a9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt @@ -1,9 +1,9 @@ package app.revanced.patches.tudortmund.lockscreen -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.tudortmund.misc.extension.sharedExtensionPatch import com.android.tools.smali.dexlib2.Opcode @@ -24,8 +24,8 @@ val showOnLockscreenPatch = bytecodePatch( compatibleWith("de.tudortmund.app") - execute { - brightnessFingerprint.method.apply { + apply { + brightnessMethod.apply { // Find the instruction where the brightness value is loaded into a register val brightnessInstruction = instructions.firstNotNullOf { instruction -> if (instruction.opcode != Opcode.IGET_OBJECT) return@firstNotNullOf null diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt index 4a9bbe297e..c5310ea85e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt @@ -13,7 +13,7 @@ val disableDashboardAdsPatch = bytecodePatch( compatibleWith("com.tumblr") - execute { + apply { // The timeline object types are filtered by their name in the TimelineObjectType enum. // This is often different from the "object_type" returned in the api (noted in comments here) arrayOf( diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt index c166cb83bf..71bdda7922 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt @@ -13,7 +13,7 @@ val disableAdFreeBannerPatch = bytecodePatch( compatibleWith("com.tumblr") - execute { + apply { // Disable the "AD_FREE_CTA_BANNER" ("Whether or not to show ad free prompt") feature flag. addFeatureFlagOverride("adFreeCtaBanner", "false") } diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt index 3a46c19b26..0454cc9efe 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt @@ -13,7 +13,7 @@ val disableInAppUpdatePatch = bytecodePatch( compatibleWith("com.tumblr") - execute { + apply { // Before checking for updates using Google Play core AppUpdateManager, the value of this feature flag is checked. // If this flag is false or the last update check was today and no update check is performed. addFeatureFlagOverride("inAppUpdate", "false") diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt index fc3d1fc8eb..0bb5a69a59 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.tumblr.annoyances.notifications -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -10,8 +10,8 @@ val disableBlogNotificationReminderPatch = bytecodePatch( ) { compatibleWith("com.tumblr") - execute { - isBlogNotifyEnabledFingerprint.method.addInstructions( + apply { + isBlogNotifyEnabledMethod.addInstructions( 0, """ # Return false for BlogNotifyCtaDialog.isEnabled() method. diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt index 67e051a7ba..e1b0994554 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt @@ -1,11 +1,13 @@ package app.revanced.patches.tumblr.annoyances.notifications -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.patch.BytecodePatchContext // The BlogNotifyCtaDialog asks you if you want to enable notifications for a blog. // It shows whenever you visit a certain blog for the second time and disables itself // if it was shown a total of 3 times (stored in app storage). // This targets the BlogNotifyCtaDialog.isEnabled() method to let it always return false. -internal val isBlogNotifyEnabledFingerprint = fingerprint { - strings("isEnabled --> ", "blog_notify_enabled") -} +internal val BytecodePatchContext.isBlogNotifyEnabledMethod by gettingFirstMethod( + "isEnabled --> ", + "blog_notify_enabled", +) diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt index 09faa17346..c447c2a59c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.tumblr.annoyances.popups -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") val disableGiftMessagePopupPatch = bytecodePatch( @@ -10,7 +10,7 @@ val disableGiftMessagePopupPatch = bytecodePatch( ) { compatibleWith("com.tumblr") - execute { - showGiftMessagePopupFingerprint.method.addInstructions(0, "return-void") + apply { + showGiftMessagePopupMethod.returnEarly() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt index 7d00f2f1bd..cd678f7cb8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt @@ -1,11 +1,13 @@ package app.revanced.patches.tumblr.annoyances.popups +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint // This method is responsible for loading and displaying the visual Layout of the Gift Message Popup. -internal val showGiftMessagePopupFingerprint = fingerprint { +internal val BytecodePatchContext.showGiftMessagePopupMethod by gettingFirstMethodDeclaratively("activity", "anchorView", "textMessage") { accessFlags(AccessFlags.FINAL, AccessFlags.PUBLIC) - returns("V") - strings("activity", "anchorView", "textMessage") -} + returnType("V") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/tv/DisableTumblrTvPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/tv/DisableTumblrTvPatch.kt index 00f8c3f373..4e66acab4d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/tv/DisableTumblrTvPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/tv/DisableTumblrTvPatch.kt @@ -5,7 +5,7 @@ import app.revanced.patches.tumblr.featureflags.addFeatureFlagOverride import app.revanced.patches.tumblr.featureflags.overrideFeatureFlagsPatch @Suppress("unused") -val disableTumblrTvPatch = bytecodePatch( +val disableTumblrTVPatch = bytecodePatch( name = "Disable Tumblr TV", description = "Removes the Tumblr TV navigation button from the bottom navigation bar.", ) { @@ -13,7 +13,7 @@ val disableTumblrTvPatch = bytecodePatch( compatibleWith("com.tumblr") - execute { + apply { addFeatureFlagOverride("tumblrTvMobileNav", "false") } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt index e24ca2884a..cc63899cc5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt @@ -1,10 +1,15 @@ package app.revanced.patches.tumblr.featureflags -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.accessFlags +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode -// This fingerprint targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature". +// This targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature". // Features seem to be Tumblr's A/B testing program. // Feature states are loaded from the server in the "api-http2.tumblr.com/v2/config" request on (first) startup. // A lot of features are returned there, but most of them do not seem to do anything (anymore). @@ -13,14 +18,13 @@ import app.revanced.patcher.fingerprint // Some features seem to be very old and never removed, though, such as Google Login. // The startIndex of the opcode pattern is at the start of the function after the arg null check. // we want to insert our instructions there. -internal val getFeatureValueFingerprint = fingerprint { +internal val BytecodePatchContext.getFeatureValueMethodMatch by composingFirstMethod("feature") { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters("L", "Z") + returnType("Ljava/lang/String;") + parameterTypes("L", "Z") opcodes( Opcode.IF_EQZ, Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT, ) - strings("feature") } 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..ed5849a77a 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 @@ -1,9 +1,9 @@ package app.revanced.patches.tumblr.featureflags -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.immutable.ImmutableMethod @@ -23,56 +23,57 @@ val overrideFeatureFlagsPatch = bytecodePatch( description = "Forcibly set the value of A/B testing features of your choice.", ) { - execute { - val configurationClass = getFeatureValueFingerprint.originalMethod.definingClass - val featureClass = getFeatureValueFingerprint.originalMethod.parameterTypes[0].toString() + apply { + getFeatureValueMethodMatch.let { match -> + val configurationClass = match.immutableMethod.definingClass + val featureClass = match.immutableMethod.parameterTypes[0].toString() - // The method we want to inject into does not have enough registers, so we inject a helper method - // and inject more instructions into it later, see addOverride. - // This is not in an extension since the unused variable would get compiled away and the method would - // get compiled to only have one register, which is not enough for our later injected instructions. - val helperMethod = ImmutableMethod( - getFeatureValueFingerprint.originalMethod.definingClass, - "getValueOverride", - listOf(ImmutableMethodParameter(featureClass, null, "feature")), - "Ljava/lang/String;", - AccessFlags.PUBLIC.value or AccessFlags.FINAL.value, - null, - null, - MutableMethodImplementation(4), - ).toMutable().apply { - // This is the equivalent of - // String featureName = feature.toString() - // - // return null - addInstructions( - 0, + // The method we want to inject into does not have enough registers, so we inject a helper method + // and inject more instructions into it later, see addOverride. + // This is not in an extension since the unused variable would get compiled away and the method would + // get compiled to only have one register, which is not enough for our later injected instructions. + val helperMethod = ImmutableMethod( + match.immutableMethod.definingClass, + "getValueOverride", + listOf(ImmutableMethodParameter(featureClass, null, "feature")), + "Ljava/lang/String;", + AccessFlags.PUBLIC.value or AccessFlags.FINAL.value, + null, + null, + MutableMethodImplementation(4), + ).toMutable().apply { + // This is the equivalent of + // String featureName = feature.toString() + // + // return null + addInstructions( + 0, + """ + # toString() the enum value + invoke-virtual {p1}, $featureClass->toString()Ljava/lang/String; + move-result-object v0 + + # !!! If you add more instructions above this line, update helperInsertIndex below! + # Here we will insert one compare & return for every registered Feature override + # This is done below in the addOverride function + + # If none of the overrides returned a value, we should return null + const/4 v0, 0x0 + return-object v0 + """, + ) + }.also { helperMethod -> + match.classDef.methods.add(helperMethod) + } + + // Here we actually insert the hook to call our helper method and return its value if it returns not null + // This is equivalent to + // String forcedValue = getValueOverride(feature) + // if (forcedValue != null) return forcedValue + val getFeatureIndex = match[0] + match.method.addInstructionsWithLabels( + getFeatureIndex, """ - # toString() the enum value - invoke-virtual {p1}, $featureClass->toString()Ljava/lang/String; - move-result-object v0 - - # !!! If you add more instructions above this line, update helperInsertIndex below! - # Here we will insert one compare & return for every registered Feature override - # This is done below in the addOverride function - - # If none of the overrides returned a value, we should return null - const/4 v0, 0x0 - return-object v0 - """, - ) - }.also { helperMethod -> - getFeatureValueFingerprint.classDef.methods.add(helperMethod) - } - - // Here we actually insert the hook to call our helper method and return its value if it returns not null - // This is equivalent to - // String forcedValue = getValueOverride(feature) - // if (forcedValue != null) return forcedValue - val getFeatureIndex = getFeatureValueFingerprint.patternMatch!!.startIndex - getFeatureValueFingerprint.method.addInstructionsWithLabels( - getFeatureIndex, - """ # Call the Helper Method with the Feature invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String; move-result-object v0 @@ -85,31 +86,32 @@ val overrideFeatureFlagsPatch = bytecodePatch( :is_null nop """, - ) - - val helperInsertIndex = 2 - addFeatureFlagOverride = { name, value -> - // For every added override, we add a few instructions in the middle of the helper method - // to check if the feature is the one we want and return the override value if it is. - // This is equivalent to - // if (featureName == {name}) return {value} - helperMethod.addInstructionsWithLabels( - helperInsertIndex, - """ - # v0 is still the string name of the currently checked feature from above - # Compare the current string with the override string - const-string v1, "$name" - invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z - move-result v1 - # If the current string is the one we want to override, we return the override value - if-eqz v1, :no_override - const-string v1, "$value" - return-object v1 - # Else we just continue... - :no_override - nop - """, ) + + val helperInsertIndex = 2 + addFeatureFlagOverride = { name, value -> + // For every added override, we add a few instructions in the middle of the helper method + // to check if the feature is the one we want and return the override value if it is. + // This is equivalent to + // if (featureName == {name}) return {value} + helperMethod.addInstructionsWithLabels( + helperInsertIndex, + """ + # v0 is still the string name of the currently checked feature from above + # Compare the current string with the override string + const-string v1, "$name" + invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z + move-result v1 + # If the current string is the one we want to override, we return the override value + if-eqz v1, :no_override + const-string v1, "$value" + return-object v1 + # Else we just continue... + :no_override + nop + """, + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt index 11616fcc92..a095e29cf8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt @@ -1,23 +1,25 @@ package app.revanced.patches.tumblr.fixes +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -import app.revanced.patcher.fingerprint -// Fingerprint for the addQueryParam method from retrofit2 -// https://github.com/square/retrofit/blob/trunk/retrofit/src/main/java/retrofit2/RequestBuilder.java#L186 -// Injecting here allows modifying dynamically set query parameters -internal val addQueryParamFingerprint = fingerprint { - parameters("Ljava/lang/String;", "Ljava/lang/String;", "Z") - strings("Malformed URL. Base: ", ", Relative: ") +// Matches the addQueryParam method from retrofit2: +// https://github.com/square/retrofit/blob/trunk/retrofit/src/main/java/retrofit2/RequestBuilder.java#L186. +// Injecting here allows modifying dynamically set query parameters. +internal val BytecodePatchContext.addQueryParamMethod by gettingFirstMethodDeclaratively("Malformed URL. Base: ", ", Relative: ") { + parameterTypes("Ljava/lang/String;", "Ljava/lang/String;", "Z") } -// Fingerprint for the parseHttpMethodAndPath method from retrofit2 +// Matches the parseHttpMethodAndPath method from retrofit2: // https://github.com/square/retrofit/blob/ebf87b10997e2136af4d335276fa950221852c64/retrofit/src/main/java/retrofit2/RequestFactory.java#L270-L302 -// Injecting here allows modifying the path/query params of API endpoints defined via annotations -internal val httpPathParserFingerprint = fingerprint { +// Injecting here allows modifying the path/query params of API endpoints defined via annotations. +internal val BytecodePatchContext.httpPathParserMethodMatch by composingFirstMethod("Only one HTTP method is allowed. Found: %s and %s.") { opcodes( Opcode.IPUT_OBJECT, Opcode.IPUT_BOOLEAN, ) - strings("Only one HTTP method is allowed. Found: %s and %s.") } 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..b6f0174f85 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 @@ -1,6 +1,6 @@ package app.revanced.patches.tumblr.fixes -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch @Suppress("unused") @@ -12,7 +12,7 @@ val fixOldVersionsPatch = bytecodePatch( ) { compatibleWith("com.tumblr") - execute { + apply { val liveQueryParameters = listOf( ",?live_now", ",?live_streaming_user_id", @@ -20,8 +20,8 @@ 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, + httpPathParserMethodMatch.method.addInstructions( + httpPathParserMethodMatch[-1] + 1, """ # urlPath = urlPath.replace(liveQueryParameter, "") const-string p1, "$liveQueryParameter" @@ -39,7 +39,7 @@ val fixOldVersionsPatch = bytecodePatch( // which would result in the path "api/me/inf0?fields[blog]=${value}" // Here we make sure that this value doesn't contain the broken query parameters. for (liveQueryParameter in liveQueryParameters) { - addQueryParamFingerprint.method.addInstructions( + addQueryParamMethod.addInstructions( 0, """ # queryParameterValue = queryParameterValue.replace(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..f61816e0d8 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 @@ -1,9 +1,9 @@ package app.revanced.patches.tumblr.timelinefilter -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.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.removeInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.tumblr.misc.extension.sharedExtensionPatch import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c @@ -23,10 +23,10 @@ val filterTimelineObjectsPatch = bytecodePatch( ) { dependsOn(sharedExtensionPatch) - execute { - val filterInsertIndex = timelineFilterExtensionFingerprint.patternMatch!!.startIndex + apply { + val filterInsertIndex = timelineFilterExtensionMethodMatch[0] - timelineFilterExtensionFingerprint.method.apply { + timelineFilterExtensionMethodMatch.method.apply { val addInstruction = getInstruction(filterInsertIndex + 1) val filterListRegister = addInstruction.registerC @@ -47,11 +47,11 @@ val filterTimelineObjectsPatch = bytecodePatch( } } - mapOf( - timelineConstructorFingerprint to 1, - postsResponseConstructorFingerprint to 2, - ).forEach { (fingerprint, timelineObjectsRegister) -> - fingerprint.method.addInstructions( + arrayOf( + timelineConstructorMethod to 1, + postsResponseConstructorMethod to 2, + ).forEach { (method, timelineObjectsRegister) -> + method.addInstructions( 0, "invoke-static {p$timelineObjectsRegister}, " + "Lapp/revanced/extension/tumblr/patches/TimelineFilterPatch;->" + diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt index b8ed3bfc4e..2f4ce8c1e3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt @@ -1,36 +1,33 @@ package app.revanced.patches.tumblr.timelinefilter -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode // This is the constructor of the PostsResponse class. -// The same applies here as with the TimelineConstructorFingerprint. -internal val postsResponseConstructorFingerprint = fingerprint { +// The same applies here as with the TimelineConstructorMethod. +internal val BytecodePatchContext.postsResponseConstructorMethod by gettingFirstMethodDeclaratively { + definingClass("/PostsResponse;") accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PUBLIC) - custom { method, classDef -> classDef.endsWith("/PostsResponse;") && method.parameters.size == 4 } + custom { parameters.size == 4 } } // This is the constructor of the Timeline class. // It receives the List as an argument with a @Json annotation, so this should be the first time // that the List is exposed in non-library code. -internal val timelineConstructorFingerprint = fingerprint { - strings("timelineObjectsList") - custom { method, classDef -> - classDef.endsWith("/Timeline;") && method.parameters[0].type == "Ljava/util/List;" - } +internal val BytecodePatchContext.timelineConstructorMethod by gettingFirstMethodDeclaratively("timelineObjectsList") { + definingClass("/Timeline;") + custom { parameters[0].type == "Ljava/util/List;" } } -// This fingerprints the extension TimelineFilterPatch.filterTimeline method. -// The opcode fingerprint is searching for +// This gets the extension method TimelineFilterPatch.filterTimeline. +// Looking for // if ("BLOCKED_OBJECT_DUMMY".equals(elementType)) iterator.remove(); -internal val timelineFilterExtensionFingerprint = fingerprint { +internal val BytecodePatchContext.timelineFilterExtensionMethodMatch by composingFirstMethod("BLOCKED_OBJECT_DUMMY") { + definingClass("/TimelineFilterPatch;") opcodes( Opcode.CONST_STRING, // "BLOCKED_OBJECT_DUMMY" Opcode.INVOKE_VIRTUAL, // HashSet.add(^) ) - strings("BLOCKED_OBJECT_DUMMY") - custom { _, classDef -> - classDef.endsWith("/TimelineFilterPatch;") - } } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt index a0622f2818..340757c58d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt @@ -1,9 +1,9 @@ package app.revanced.patches.twitch.ad.audio -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference @@ -11,7 +11,8 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.settingsPatch -val audioAdsPatch = bytecodePatch( +@Suppress("unused") +val blockAudioAdsPatch = bytecodePatch( name = "Block audio ads", description = "Blocks audio ads in streams and VODs.", ) { @@ -23,7 +24,7 @@ val audioAdsPatch = bytecodePatch( compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0")) - execute { + apply { addResources("twitch", "ad.audio.audioAdsPatch") PreferenceScreen.ADS.CLIENT_SIDE.addPreferences( @@ -31,7 +32,7 @@ val audioAdsPatch = bytecodePatch( ) // Block playAds call - audioAdsPresenterPlayFingerprint.method.addInstructionsWithLabels( + audioAdsPresenterPlayMethod.addInstructionsWithLabels( 0, """ invoke-static { }, Lapp/revanced/extension/twitch/patches/AudioAdsPatch;->shouldBlockAudioAds()Z @@ -39,7 +40,7 @@ val audioAdsPatch = bytecodePatch( if-eqz v0, :show_audio_ads return-void """, - ExternalLabel("show_audio_ads", audioAdsPresenterPlayFingerprint.method.getInstruction(0)), + ExternalLabel("show_audio_ads", audioAdsPresenterPlayMethod.getInstruction(0)), ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt index 21e9cb6d2a..4828977fef 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.twitch.ad.audio -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val audioAdsPresenterPlayFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("AudioAdsPlayerPresenter;") && method.name == "playAd" - } +internal val BytecodePatchContext.audioAdsPresenterPlayMethod by gettingFirstMethodDeclaratively { + name("playAd") + definingClass("AudioAdsPlayerPresenter;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt index 8519b90ced..57bb6635da 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt @@ -1,35 +1,36 @@ package app.revanced.patches.twitch.ad.embedded -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.shared.misc.settings.preference.ListPreference -import app.revanced.patches.twitch.ad.video.videoAdsPatch +import app.revanced.patches.twitch.ad.video.blockVideoAdsPatch import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.settingsPatch -val embeddedAdsPatch = bytecodePatch( +@Suppress("unused") +val blockEmbeddedAdsPatch = bytecodePatch( name = "Block embedded ads", description = "Blocks embedded stream ads using services like Luminous or PurpleAdBlocker.", ) { dependsOn( - videoAdsPatch, + blockVideoAdsPatch, sharedExtensionPatch, settingsPatch, ) compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0")) - execute { + apply { addResources("twitch", "ad.embedded.embeddedAdsPatch") PreferenceScreen.ADS.SURESTREAM.addPreferences( ListPreference("revanced_block_embedded_ads"), ) - // Inject OkHttp3 application interceptor - createsUsherClientFingerprint.method.addInstructions( + // Inject OkHttp3 application interceptor. + createsUsherClientMethod.addInstructions( 3, """ invoke-static {}, Lapp/revanced/extension/twitch/patches/EmbeddedAdsPatch;->createRequestInterceptor()Lapp/revanced/extension/twitch/api/RequestInterceptor; diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt index 3e9853bd61..130c797c78 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.twitch.ad.embedded -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val createsUsherClientFingerprint = fingerprint { - custom { method, _ -> - method.name == "buildOkHttpClient" && method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") - } +internal val BytecodePatchContext.createsUsherClientMethod by gettingFirstMethodDeclaratively { + name("buildOkHttpClient") + definingClass("Ltv/twitch/android/network/OkHttpClientFactory;") } 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..648be7f7ef 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 @@ -1,11 +1,12 @@ package app.revanced.patches.twitch.ad.shared.util -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.firstClassDefOrNull import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel fun adPatch( conditionCall: String, @@ -29,10 +30,10 @@ fun adPatch( classDefType: String, methodNames: Set, returnMethod: ReturnMethod, - ) = with(classBy { classDefType == it.type }?.mutableClass) { - this ?: return false + ): Boolean { + val classDef = firstClassDefOrNull(classDefType) ?: return false - methods.filter { it.name in methodNames }.forEach { + classDef.methods.filter { it.name in methodNames }.forEach { method -> val retInstruction = when (returnMethod.returnType) { 'V' -> "return-void" 'Z' -> @@ -44,17 +45,17 @@ fun adPatch( else -> throw NotImplementedError() } - it.addInstructionsWithLabels( + method.addInstructionsWithLabels( 0, """ ${createConditionInstructions("v0")} $retInstruction """, - ExternalLabel(skipLabelName, it.getInstruction(0)), + ExternalLabel(skipLabelName, method.getInstruction(0)), ) } - true + return true } block(::createConditionInstructions, BytecodePatchContext::blockMethods) diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt index d03449733f..53b5e9dbec 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt @@ -1,28 +1,25 @@ package app.revanced.patches.twitch.ad.video -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val checkAdEligibilityLambdaFingerprint = fingerprint { - returns("Lio/reactivex/Single;") - parameters("L") - custom { method, _ -> - method.definingClass.endsWith("/AdEligibilityFetcher;") && - method.name == "shouldRequestAd" - } +internal val BytecodePatchContext.checkAdEligibilityLambdaMethod by gettingFirstMethodDeclaratively { + name("shouldRequestAd") + definingClass("/AdEligibilityFetcher;") + returnType("Lio/reactivex/Single;") + parameterTypes("L") } -internal val contentConfigShowAdsFingerprint = fingerprint { - returns("Z") - parameters() - custom { method, _ -> - method.definingClass.endsWith("/ContentConfigData;") && method.name == "getShowAds" - } +internal val BytecodePatchContext.contentConfigShowAdsMethod by gettingFirstMethodDeclarativelyOrNull { + name("getShowAds") + definingClass("/ContentConfigData;") + returnType("Z") + parameterTypes() } -internal val getReadyToShowAdFingerprint = fingerprint { - returns("Ltv/twitch/android/core/mvp/presenter/StateAndAction;") - parameters("L", "L") - custom { method, _ -> - method.definingClass.endsWith("/StreamDisplayAdsPresenter;") && method.name == "getReadyToShowAdOrAbort" - } +internal val BytecodePatchContext.getReadyToShowAdMethod by gettingFirstMethodDeclaratively { + name("getReadyToShowAdOrAbort") + definingClass("/StreamDisplayAdsPresenter;") + returnType("Ltv/twitch/android/core/mvp/presenter/StateAndAction;") + parameterTypes("L", "L") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt index 6665db8536..9f2faa45f6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt @@ -1,10 +1,10 @@ package app.revanced.patches.twitch.ad.video -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.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference @@ -14,7 +14,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.settingsPatch -val videoAdsPatch = bytecodePatch( +val blockVideoAdsPatch = bytecodePatch( name = "Block video ads", description = "Blocks video ads in streams and VODs.", ) { @@ -27,7 +27,7 @@ val videoAdsPatch = bytecodePatch( addResourcesPatch, adPatch(conditionCall, skipLabelName) { createConditionInstructions, blockMethods -> - execute { + apply { addResources("twitch", "ad.video.videoAdsPatch") PreferenceScreen.ADS.CLIENT_SIDE.addPreferences( @@ -111,7 +111,7 @@ val videoAdsPatch = bytecodePatch( ) // Pretend our player is ineligible for all ads. - checkAdEligibilityLambdaFingerprint.method.addInstructionsWithLabels( + checkAdEligibilityLambdaMethod.addInstructionsWithLabels( 0, """ ${createConditionInstructions("v0")} @@ -122,13 +122,13 @@ val videoAdsPatch = bytecodePatch( """, ExternalLabel( skipLabelName, - checkAdEligibilityLambdaFingerprint.method.getInstruction(0), + checkAdEligibilityLambdaMethod.getInstruction(0), ), ) val adFormatDeclined = "Ltv/twitch/android/shared/display/ads/theatre/StreamDisplayAdsPresenter\$Action\$AdFormatDeclined;" - getReadyToShowAdFingerprint.method.addInstructionsWithLabels( + getReadyToShowAdMethod.addInstructionsWithLabels( 0, """ ${createConditionInstructions("v0")} @@ -137,12 +137,12 @@ val videoAdsPatch = bytecodePatch( move-result-object p1 return-object p1 """, - ExternalLabel(skipLabelName, getReadyToShowAdFingerprint.method.getInstruction(0)), + ExternalLabel(skipLabelName, getReadyToShowAdMethod.getInstruction(0)), ) // Spoof showAds JSON field. // Late versions of the app don't have the method anymore. - contentConfigShowAdsFingerprint.methodOrNull?.addInstructions( + contentConfigShowAdsMethod?.addInstructions( 0, """ ${createConditionInstructions("v0")} 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..9dbeb760bb 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 @@ -1,24 +1,23 @@ package app.revanced.patches.twitch.chat.antidelete -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val chatUtilCreateDeletedSpanFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("ChatUtil\$Companion;") && method.name == "createDeletedSpanFromChatMessageSpan" - } +internal val BytecodePatchContext.chatUtilCreateDeletedSpanMethod by gettingFirstMethodDeclaratively { + name("createDeletedSpanFromChatMessageSpan") + definingClass("ChatUtil\$Companion;") } -internal val deletedMessageClickableSpanCtorFingerprint = fingerprint { +internal val BytecodePatchContext.deletedMessageClickableSpanCtorMethod by gettingFirstMethodDeclaratively { + definingClass("DeletedMessageClickableSpan;") accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - custom { _, classDef -> - classDef.endsWith("DeletedMessageClickableSpan;") - } } -internal val setHasModAccessFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("DeletedMessageClickableSpan;") && method.name == "setHasModAccess" - } +internal val BytecodePatchContext.setHasModAccessMethod by gettingFirstMethodDeclaratively { + name("setHasModAccess") + definingClass("DeletedMessageClickableSpan;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt index 7bc35a1316..13f93f8bcf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt @@ -1,10 +1,10 @@ package app.revanced.patches.twitch.chat.antidelete -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.ListPreference @@ -12,6 +12,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.settingsPatch +@Suppress("unused") val showDeletedMessagesPatch = bytecodePatch( name = "Show deleted messages", description = "Shows deleted chat messages behind a clickable spoiler.", @@ -30,15 +31,15 @@ val showDeletedMessagesPatch = bytecodePatch( if-eqz $register, :no_spoiler """ - execute { + apply { addResources("twitch", "chat.antidelete.showDeletedMessagesPatch") PreferenceScreen.CHAT.GENERAL.addPreferences( - ListPreference("revanced_show_deleted_messages") + ListPreference("revanced_show_deleted_messages"), ) // Spoiler mode: Force set hasModAccess member to true in constructor - deletedMessageClickableSpanCtorFingerprint.method.apply { + deletedMessageClickableSpanCtorMethod.apply { addInstructionsWithLabels( implementation!!.instructions.lastIndex, /* place in front of return-void */ """ @@ -51,10 +52,10 @@ val showDeletedMessagesPatch = bytecodePatch( } // Spoiler mode: Disable setHasModAccess setter - setHasModAccessFingerprint.method.addInstruction(0, "return-void") + setHasModAccessMethod.addInstruction(0, "return-void") // Cross-out mode: Reformat span of deleted message - chatUtilCreateDeletedSpanFingerprint.method.apply { + chatUtilCreateDeletedSpanMethod.apply { addInstructionsWithLabels( 0, """ diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt index d27a11c25c..eaea93417a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt @@ -1,16 +1,17 @@ package app.revanced.patches.twitch.chat.autoclaim -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.settingsPatch +@Suppress("unused") val autoClaimChannelPointsPatch = bytecodePatch( name = "Auto claim channel points", description = "Automatically claim Channel Points.", @@ -22,14 +23,14 @@ val autoClaimChannelPointsPatch = bytecodePatch( compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0")) - execute { + apply { addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch") PreferenceScreen.CHAT.GENERAL.addPreferences( SwitchPreference("revanced_auto_claim_channel_points"), ) - communityPointsButtonViewDelegateFingerprint.method.apply { + communityPointsButtonViewDelegateMethod.apply { val lastIndex = instructions.lastIndex addInstructionsWithLabels( lastIndex, // place in front of return-void diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt index 80abc9ac41..3aeb32daf2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt @@ -1,10 +1,11 @@ package app.revanced.patches.twitch.chat.autoclaim -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val communityPointsButtonViewDelegateFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("CommunityPointsButtonViewDelegate;") && - method.name == "showClaimAvailable" - } +internal val BytecodePatchContext.communityPointsButtonViewDelegateMethod by gettingFirstMethodDeclaratively { + name("showClaimAvailable") + definingClass("CommunityPointsButtonViewDelegate;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt index 34967b9cac..c0686d8b44 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt @@ -1,6 +1,6 @@ package app.revanced.patches.twitch.debug -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -9,6 +9,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.settingsPatch +@Suppress("ObjectPropertyName") val debugModePatch = bytecodePatch( name = "Debug mode", description = "Enables Twitch's internal debugging mode.", @@ -22,7 +23,7 @@ val debugModePatch = bytecodePatch( compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0")) - execute { + apply { addResources("twitch", "debug.debugModePatch") PreferenceScreen.MISC.OTHER.addPreferences( @@ -30,11 +31,11 @@ val debugModePatch = bytecodePatch( ) listOf( - isDebugConfigEnabledFingerprint, - isOmVerificationEnabledFingerprint, - shouldShowDebugOptionsFingerprint, - ).forEach { fingerprint -> - fingerprint.method.addInstructions( + isDebugConfigEnabledMethod, + isOmVerificationEnabledMethod, + shouldShowDebugOptionsMethod, + ).forEach { method -> + method.addInstructions( 0, """ invoke-static {}, Lapp/revanced/extension/twitch/patches/DebugModePatch;->isDebugModeEnabled()Z diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt index 665180c19b..e8fd075d63 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt @@ -1,21 +1,21 @@ package app.revanced.patches.twitch.debug -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val isDebugConfigEnabledFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/BuildConfigUtil;") && method.name == "isDebugConfigEnabled" - } +internal val BytecodePatchContext.isDebugConfigEnabledMethod by gettingFirstMethodDeclaratively { + name("isDebugConfigEnabled") + definingClass("/BuildConfigUtil;") } -internal val isOmVerificationEnabledFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/BuildConfigUtil;") && method.name == "isOmVerificationEnabled" - } +internal val BytecodePatchContext.isOmVerificationEnabledMethod by gettingFirstMethodDeclaratively { + name("isOmVerificationEnabled") + definingClass("/BuildConfigUtil;") } -internal val shouldShowDebugOptionsFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/BuildConfigUtil;") && method.name == "shouldShowDebugOptions" - } +internal val BytecodePatchContext.shouldShowDebugOptionsMethod by gettingFirstMethodDeclaratively { + name("shouldShowDebugOptions") + definingClass("/BuildConfigUtil;") } 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/twitch/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt index 43d5bb39bf..adbeb1c196 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt @@ -1,34 +1,28 @@ package app.revanced.patches.twitch.misc.settings -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val menuGroupsOnClickFingerprint = fingerprint { +internal val BytecodePatchContext.menuGroupsOnClickMethod by gettingFirstMethodDeclaratively { + name { contains("render") } + definingClass("/SettingsMenuViewDelegate;") accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC, AccessFlags.FINAL) - returns("V") - parameters("L", "L", "L") - custom { method, classDef -> - classDef.endsWith("/SettingsMenuViewDelegate;") && - method.name.contains("render") - } + returnType("V") + parameterTypes("L", "L", "L") } -internal val menuGroupsUpdatedFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/SettingsMenuPresenter\$Event\$MenuGroupsUpdated;") && - method.name == "" - } +internal val BytecodePatchContext.menuGroupsUpdatedMethod by gettingFirstMethodDeclaratively { + name("") + definingClass("/SettingsMenuPresenter\$Event\$MenuGroupsUpdated;") } -internal val settingsActivityOnCreateFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/SettingsActivity;") && - method.name == "onCreate" - } +internal val BytecodePatchContext.settingsActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("/SettingsActivity;") } -internal val settingsMenuItemEnumFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("/SettingsMenuItem;") && method.name == "" - } +internal val BytecodePatchContext.settingsMenuItemEnumMethod by gettingFirstMethodDeclaratively { + name("") + definingClass("/SettingsMenuItem;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt index 66d790718b..e99788f5d9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt @@ -1,19 +1,17 @@ package app.revanced.patches.twitch.misc.settings -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableField.Companion.toMutable +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable -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.settings.preference.BasePreference -import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen -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.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.* import app.revanced.patches.shared.misc.settings.settingsPatch import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import com.android.tools.smali.dexlib2.AccessFlags @@ -50,7 +48,7 @@ val settingsPatch = bytecodePatch( compatibleWith("tv.twitch.android.app"("16.9.1")) - execute { + apply { addResources("twitch", "misc.settings.settingsPatch") preferences += NonInteractivePreference( @@ -74,23 +72,22 @@ val settingsPatch = bytecodePatch( ) // Hook onCreate to handle fragment creation. - val insertIndex = settingsActivityOnCreateFingerprint.method.implementation!!.instructions.size - 2 - settingsActivityOnCreateFingerprint.method.addInstructionsWithLabels( - insertIndex, - """ - invoke-static { p0 }, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingsCreation(Landroidx/appcompat/app/AppCompatActivity;)Z - move-result v0 - if-eqz v0, :no_rv_settings_init - return-void - """, - ExternalLabel( - "no_rv_settings_init", - settingsActivityOnCreateFingerprint.method.getInstruction(insertIndex), - ), - ) + settingsActivityOnCreateMethod.apply { + val insertIndex = instructions.size - 2 + addInstructionsWithLabels( + insertIndex, + """ + invoke-static { p0 }, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingsCreation(Landroidx/appcompat/app/AppCompatActivity;)Z + move-result v0 + if-eqz v0, :no_rv_settings_init + return-void + """, + ExternalLabel("no_rv_settings_init", getInstruction(insertIndex)), + ) + } // Create new menu item for settings menu. - fun Fingerprint.injectMenuItem( + fun MutableMethod.injectMenuItem( name: String, value: Int, titleResourceName: String, @@ -99,7 +96,7 @@ val settingsPatch = bytecodePatch( // Add new static enum member field classDef.staticFields.add( ImmutableField( - method.definingClass, + definingClass, name, MENU_ITEM_ENUM_CLASS_DESCRIPTOR, AccessFlags.PUBLIC.value or @@ -113,8 +110,8 @@ val settingsPatch = bytecodePatch( ) // Add initializer for the new enum member - method.addInstructions( - method.implementation!!.instructions.size - 4, + addInstructions( + instructions.size - 4, """ new-instance v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR const-string v1, "$titleResourceName" @@ -131,7 +128,7 @@ val settingsPatch = bytecodePatch( ) } - settingsMenuItemEnumFingerprint.injectMenuItem( + settingsMenuItemEnumMethod.injectMenuItem( REVANCED_SETTINGS_MENU_ITEM_NAME, REVANCED_SETTINGS_MENU_ITEM_ID, REVANCED_SETTINGS_MENU_ITEM_TITLE_RES, @@ -139,7 +136,7 @@ val settingsPatch = bytecodePatch( ) // Intercept settings menu creation and add new menu item. - menuGroupsUpdatedFingerprint.method.addInstructions( + menuGroupsUpdatedMethod.addInstructions( 0, """ sget-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$REVANCED_SETTINGS_MENU_ITEM_NAME:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR @@ -149,7 +146,7 @@ val settingsPatch = bytecodePatch( ) // Intercept onclick events for the settings menu - menuGroupsOnClickFingerprint.method.addInstructionsWithLabels( + menuGroupsOnClickMethod.addInstructionsWithLabels( 0, """ invoke-static {p1}, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuOnClick(Ljava/lang/Enum;)Z @@ -161,12 +158,12 @@ val settingsPatch = bytecodePatch( """, ExternalLabel( "no_rv_settings_onclick", - menuGroupsOnClickFingerprint.method.getInstruction(0), + menuGroupsOnClickMethod.getInstruction(0), ), ) } - finalize { + afterDependents { PreferenceScreen.close() } } 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..52afcfa187 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 @@ -1,27 +1,25 @@ package app.revanced.patches.twitter.interaction.downloads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val buildMediaOptionsSheetFingerprint = fingerprint { +internal val BytecodePatchContext.buildMediaOptionsSheetMethodMatch by composingFirstMethod("mediaEntity", "media_options_sheet") { opcodes( Opcode.IF_EQ, Opcode.SGET_OBJECT, Opcode.GOTO_16, Opcode.NEW_INSTANCE, ) - strings("mediaEntity", "media_options_sheet") } -internal val constructMediaOptionsSheetFingerprint = fingerprint { +internal val BytecodePatchContext.constructMediaOptionsSheetMethodMatch by composingFirstMethod("captionsState") { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - strings("captionsState") } -internal val showDownloadVideoUpsellBottomSheetFingerprint = fingerprint { - returns("Z") - strings("mediaEntity", "url") +internal val BytecodePatchContext.showDownloadVideoUpsellBottomSheetMethodMatch by composingFirstMethod("mediaEntity") { + returnType("Z") opcodes(Opcode.IF_EQZ) + instructions("url"(String::contains)) } 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..99165494c5 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 @@ -1,13 +1,8 @@ package app.revanced.patches.twitter.interaction.downloads -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.CompositeMatch +import app.revanced.patcher.extensions.* import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -19,22 +14,22 @@ val unlockDownloadsPatch = bytecodePatch( ) { compatibleWith("com.twitter.android") - execute { - fun Fingerprint.patch(getRegisterAndIndex: Fingerprint.() -> Pair) { + apply { + fun CompositeMatch.patch(getRegisterAndIndex: CompositeMatch.() -> Pair) { val (index, register) = getRegisterAndIndex() method.addInstruction(index, "const/4 v$register, 0x1") } // Allow downloads for non-premium users. - showDownloadVideoUpsellBottomSheetFingerprint.patch { - val checkIndex = patternMatch!!.startIndex + showDownloadVideoUpsellBottomSheetMethodMatch.patch { + val checkIndex = showDownloadVideoUpsellBottomSheetMethodMatch[0] val register = method.getInstruction(checkIndex).registerA checkIndex to register } // Force show the download menu item. - constructMediaOptionsSheetFingerprint.patch { + constructMediaOptionsSheetMethodMatch.patch { val showDownloadButtonIndex = method.instructions.lastIndex - 1 val register = method.getInstruction(showDownloadButtonIndex).registerA @@ -42,25 +37,26 @@ val unlockDownloadsPatch = bytecodePatch( } // Make GIFs downloadable. - val patternMatch = buildMediaOptionsSheetFingerprint.patternMatch!! - buildMediaOptionsSheetFingerprint.method.apply { - val checkMediaTypeIndex = patternMatch.startIndex - val checkMediaTypeInstruction = getInstruction(checkMediaTypeIndex) + buildMediaOptionsSheetMethodMatch.let { + it.method.apply { + val checkMediaTypeIndex = it[0] + 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[-1])), + ) - // 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/layout/viewcount/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/Fingerprints.kt index 625b6f0bb7..e1d0b05ea5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/Fingerprints.kt @@ -1,8 +1,10 @@ package app.revanced.patches.twitter.layout.viewcount -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType + +internal val BytecodePatchContext.viewCountsEnabledMethod by gettingFirstMethodDeclaratively("view_counts_public_visibility_enabled") { + returnType("Z") -internal val viewCountsEnabledFingerprint = fingerprint { - returns("Z") - strings("view_counts_public_visibility_enabled") } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt index deee9749fa..d95a340abc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.twitter.layout.viewcount -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly @Suppress("unused") val hideViewCountPatch = bytecodePatch( @@ -11,13 +11,7 @@ val hideViewCountPatch = bytecodePatch( ) { compatibleWith("com.twitter.android") - execute { - viewCountsEnabledFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x0 - return v0 - """, - ) + apply { + viewCountsEnabledMethod.returnEarly() } } 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..c079fcf157 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 @@ -14,10 +14,10 @@ val dynamicColorPatch = resourcePatch( "com.twitter.android"( "10.60.0-release.0", "10.86.0-release.0", - ) + ), ) - execute { + apply { val resDirectory = get("res") if (!resDirectory.isDirectory) throw PatchException("The res folder can not be found.") @@ -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/extension/hooks/ApplicationInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/extension/hooks/ApplicationInitHook.kt index 13a1590a7e..aa037d7f21 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/extension/hooks/ApplicationInitHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/extension/hooks/ApplicationInitHook.kt @@ -1,10 +1,10 @@ package app.revanced.patches.twitter.misc.extension.hooks +import app.revanced.patcher.definingClass +import app.revanced.patcher.name import app.revanced.patches.shared.misc.extension.extensionHook -internal val applicationInitHook = - extensionHook { - custom { method, classDef -> - classDef.type == "Lcom/twitter/app/TwitterApplication;" && method.name == "onCreate" - } - } \ No newline at end of file +internal val applicationInitHook = extensionHook { + name("onCreate") + definingClass("Lcom/twitter/app/TwitterApplication;") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt index 682bb4f379..e1aac1db77 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.twitter.misc.hook import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.twitter.misc.hook.json.JsonHook import app.revanced.patches.twitter.misc.hook.json.addJsonHook +import app.revanced.patches.twitter.misc.hook.json.jsonHook import app.revanced.patches.twitter.misc.hook.json.jsonHookPatch fun hookPatch( @@ -15,10 +15,10 @@ fun hookPatch( "com.twitter.android"( "10.60.0-release.0", "10.86.0-release.0", - ) + ), ) - execute { - addJsonHook(JsonHook(hookClassDescriptor)) + apply { + addJsonHook(jsonHook(hookClassDescriptor)) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt index 337aeb5670..1f5f31264d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt @@ -1,27 +1,34 @@ package app.revanced.patches.twitter.misc.hook.json -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef +import kotlin.properties.ReadOnlyProperty -internal val jsonHookPatchFingerprint = fingerprint { - opcodes( - Opcode.INVOKE_INTERFACE, // Add dummy hook to hooks list. - // Add hooks to the hooks list. - Opcode.INVOKE_STATIC, // Call buildList. - ) - custom { method, _ -> method.name == "" } +internal val BytecodePatchContext.jsonHookPatchMethodMatch by ReadOnlyProperty { context, _ -> + context.firstImmutableClassDef(JSON_HOOK_PATCH_CLASS_DESCRIPTOR).firstMethodComposite { + name("") + opcodes( + Opcode.INVOKE_INTERFACE, // Add dummy hook to hooks list. + // Add hooks to the hooks list. + Opcode.INVOKE_STATIC, // Call buildList. + ) + } } -internal val jsonInputStreamFingerprint = fingerprint { - custom { method, _ -> - if (method.parameterTypes.isEmpty()) { +context(_: BytecodePatchContext) +internal fun ClassDef.getJsonInputStreamMethod() = firstMethodDeclaratively { + custom { + if (parameterTypes.isEmpty()) { false } else { - method.parameterTypes.first() == "Ljava/io/InputStream;" + parameterTypes.first() == "Ljava/io/InputStream;" } } } -internal val loganSquareFingerprint = fingerprint { - custom { _, classDef -> classDef.endsWith("LoganSquare;") } +internal val BytecodePatchContext.loganSquareClassDef by gettingFirstImmutableClassDef { + type.endsWith("LoganSquare;") } 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..e4fce19f2e 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 @@ -1,7 +1,8 @@ package app.revanced.patches.twitter.misc.hook.json -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstructions +import app.revanced.patcher.firstImmutableClassDef import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch @@ -14,30 +15,27 @@ import java.io.InvalidClassException * * @param jsonHook The [JsonHook] to add. */ -context(BytecodePatchContext) -fun addJsonHook( +fun BytecodePatchContext.addJsonHook( jsonHook: JsonHook, ) { if (jsonHook.added) return - jsonHookPatchFingerprint.method.apply { - // Insert hooks right before calling buildList. - val insertIndex = jsonHookPatchFingerprint.patternMatch!!.endIndex + // Insert hooks right before calling buildList. + val insertIndex = jsonHookPatchMethodMatch[-1] - addInstructions( - insertIndex, - """ - sget-object v1, ${jsonHook.descriptor}->INSTANCE:${jsonHook.descriptor} - invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z - """, - ) - } + jsonHookPatchMethodMatch.method.addInstructions( + insertIndex, + """ + sget-object v1, ${jsonHook.descriptor}->INSTANCE:${jsonHook.descriptor} + invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z + """, + ) jsonHook.added = true } private const val JSON_HOOK_CLASS_NAMESPACE = "app/revanced/extension/twitter/patches/hook/json" -private const val JSON_HOOK_PATCH_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/JsonHookPatch;" +internal const val JSON_HOOK_PATCH_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/JsonHookPatch;" private const val BASE_PATCH_CLASS_NAME = "BaseJsonHook" private const val JSON_HOOK_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/$BASE_PATCH_CLASS_NAME;" @@ -46,26 +44,20 @@ val jsonHookPatch = bytecodePatch( ) { dependsOn(sharedExtensionPatch) - 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.") - - matchOrNull(jsonHookPatch.immutableClass) - ?: throw PatchException("Unexpected extension.") - } + apply { + jsonHookPatchMethodMatch.methodOrNull + ?: throw PatchException("Unexpected extension.") val jsonFactoryClassDef = - loganSquareFingerprint.originalClassDef // Conveniently find the type to hook a method in, via a named field. + loganSquareClassDef // Conveniently find the type to hook a method in, via a named field. .fields .firstOrNull { it.name == "JSON_FACTORY" } ?.type - .let { type -> classes.find { it.type == type } } + ?.let { type -> firstImmutableClassDef(type) } ?: throw PatchException("Could not find required class.") // Hook the methods first parameter. - jsonInputStreamFingerprint.match(jsonFactoryClassDef).method.addInstructions( + jsonFactoryClassDef.getJsonInputStreamMethod().addInstructions( 0, """ invoke-static { p1 }, $JSON_HOOK_PATCH_CLASS_DESCRIPTOR->parseJsonHook(Ljava/io/InputStream;)Ljava/io/InputStream; @@ -74,16 +66,20 @@ val jsonHookPatch = bytecodePatch( ) } - finalize { + afterDependents { // Remove hooks.add(dummyHook). - jsonHookPatchFingerprint.method.apply { - val addDummyHookIndex = jsonHookPatchFingerprint.patternMatch!!.endIndex - 2 + val addDummyHookIndex = jsonHookPatchMethodMatch[-1] - removeInstructions(addDummyHookIndex, 2) - } + jsonHookPatchMethodMatch.method.removeInstructions(addDummyHookIndex, 2) } } +class JsonHook internal constructor( + internal val descriptor: String, +) { + internal var added = false +} + /** * Create a hook class. * The class has to extend on **JsonHook**. @@ -92,22 +88,18 @@ val jsonHookPatch = bytecodePatch( * @param descriptor The class descriptor of the hook. * @throws ClassNotFoundException If the class could not be found. */ -context(BytecodePatchContext) -class JsonHook( - internal val descriptor: String, -) { - internal var added = false - - init { - classBy { it.type == descriptor }?.let { - it.mutableClass.also { classDef -> - if ( - classDef.superclass != JSON_HOOK_CLASS_DESCRIPTOR || - !classDef.fields.any { field -> field.name == "INSTANCE" } - ) { - throw InvalidClassException(classDef.type, "Not a hook class") - } +context(context: BytecodePatchContext) +fun jsonHook(descriptor: String): JsonHook { + context.firstImmutableClassDef(descriptor).let { + it.also { classDef -> + if ( + classDef.superclass != JSON_HOOK_CLASS_DESCRIPTOR || + !classDef.fields.any { field -> field.name == "INSTANCE" } + ) { + throw InvalidClassException(classDef.type, "Not a hook class") } - } ?: throw ClassNotFoundException("Failed to find hook class $descriptor") + } } + + return JsonHook(JSON_HOOK_CLASS_DESCRIPTOR) } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt index e69a449652..77660b0118 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt @@ -1,12 +1,10 @@ package app.revanced.patches.twitter.misc.links -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.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.stringOption -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.patches.twitter.misc.extension.sharedExtensionPatch import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.returnEarly @@ -19,9 +17,8 @@ import java.util.logging.Logger internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/twitter/patches/links/ChangeLinkSharingDomainPatch;" internal val domainNameOption = stringOption( - key = "domainName", default = "fxtwitter.com", - title = "Domain name", + name = "Domain name", description = "The domain name to use when sharing links.", required = true, ) { @@ -33,7 +30,7 @@ internal val domainNameOption = stringOption( InetAddress.getByName(it) } catch (_: UnknownHostException) { Logger.getLogger(this::class.java.name).warning( - "Host \"$it\" did not resolve to any domain." + "Host \"$it\" did not resolve to any domain.", ) } catch (_: Exception) { // Must ignore any kind of exception. Trying to resolve network @@ -46,7 +43,7 @@ internal val domainNameOption = stringOption( // TODO restore this once Manager uses a fixed version of Patcher /* internal val changeLinkSharingDomainResourcePatch = resourcePatch { - execute { + apply { val domainName = domainNameOption.value!! val shareLinkTemplate = "https://$domainName/%1\$s/status/%2\$s" @@ -63,9 +60,9 @@ internal val changeLinkSharingDomainResourcePatch = resourcePatch { @Suppress("unused") val changeLinkSharingDomainPatch = bytecodePatch( - name = PATCH_NAME_CHANGE_LINK_SHARING_DOMAIN, - description = "$PATCH_DESCRIPTION_CHANGE_LINK_SHARING_DOMAIN Including this patch can prevent making posts that quote other posts.", - use = false + name = "Change link sharing domain", + description = "Replaces the domain name of shared links. Using this patch can prevent making posts that quote other posts.", + use = false, ) { dependsOn( sharedExtensionPatch, @@ -75,28 +72,28 @@ val changeLinkSharingDomainPatch = bytecodePatch( "com.twitter.android"( "10.60.0-release.0", "10.86.0-release.0", - ) + ), ) val domainName by domainNameOption() - execute { + apply { // Replace the domain name in the link sharing extension methods. - linkSharingDomainHelperFingerprint.method.returnEarly(domainName!!) + linkSharingDomainHelperMethod.returnEarly(domainName!!) // Replace the domain name when copying a link with "Copy link" button. - linkBuilderFingerprint.method.addInstructions( + linkBuilderMethod.addInstructions( 0, """ invoke-static { p0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->formatLink(JLjava/lang/String;)Ljava/lang/String; move-result-object p0 return-object p0 - """ + """, ) // TODO remove this once changeLinkSharingDomainResourcePatch is restored // Replace the domain name in the "Share via..." dialog. - linkResourceGetterFingerprint.method.apply { + linkResourceGetterMethod.apply { val templateIdConstIndex = indexOfFirstInstructionOrThrow(Opcode.CONST) // Format the link with the new domain name register (1 instruction below the const). @@ -107,7 +104,7 @@ val changeLinkSharingDomainPatch = bytecodePatch( replaceInstruction( formatLinkCallIndex, "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->" + - "formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;", + "formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;", ) } } 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..5bc19f665b 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 @@ -1,37 +1,29 @@ package app.revanced.patches.twitter.misc.links -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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") -} +internal val BytecodePatchContext.sanitizeSharingLinksMethod by gettingFirstMethod( + "", + "shareParam", + "sessionToken", +) { returnType == "Ljava/lang/String;" } // Returns a shareable link string based on a tweet ID and a username. -internal val linkBuilderFingerprint = fingerprint { - strings("/%1\$s/status/%2\$d") -} +internal val BytecodePatchContext.linkBuilderMethod by gettingFirstMethod($$"/%1$s/status/%2$d") // TODO remove this once changeLinkSharingDomainResourcePatch is restored // Returns a shareable link for the "Share via..." dialog. -internal val linkResourceGetterFingerprint = fingerprint { +internal val BytecodePatchContext.linkResourceGetterMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Landroid/content/res/Resources;") - custom { _, classDef -> - classDef.fields.any { field -> - field.type.startsWith("Lcom/twitter/model/core/") - } + parameterTypes("Landroid/content/res/Resources;") + custom { + immutableClassDef.anyField { type.startsWith("Lcom/twitter/model/core/") } } } -internal val linkSharingDomainHelperFingerprint = fingerprint { - custom { method, classDef -> - method.name == "getShareDomain" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } +internal val BytecodePatchContext.linkSharingDomainHelperMethod by gettingFirstMethodDeclaratively { + name("getShareDomain") + definingClass(EXTENSION_CLASS_DESCRIPTOR) } 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/twitter/misc/links/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt index 7856bfe966..26ec58b1b8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt @@ -1,24 +1,22 @@ package app.revanced.patches.twitter.misc.links -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS -import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS @Suppress("unused") val sanitizeSharingLinksPatch = bytecodePatch( - name = PATCH_NAME_SANITIZE_SHARING_LINKS, - description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS, + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from shared links.", ) { compatibleWith( "com.twitter.android"( "10.60.0-release.0", "10.86.0-release.0", - ) + ), ) - execute { - sanitizeSharingLinksFingerprint.method.addInstructions( + apply { + sanitizeSharingLinksMethod.addInstructions( 0, """ # Method takes in a link (string, param 0) and then appends the tracking query params, diff --git a/patches/src/main/kotlin/app/revanced/patches/viber/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/viber/ads/Fingerprints.kt index 29b752cf84..7274ec124f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/viber/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/viber/ads/Fingerprints.kt @@ -1,7 +1,14 @@ package app.revanced.patches.viber.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.Opcode -internal val findAdStringFingerprint = fingerprint { - strings("viber_plus_debug_ads_free_flag") +internal val BytecodePatchContext.findAdStringMethodMatch by composingFirstMethod { + instructions( + Opcode.NEW_INSTANCE(), + "viber_plus_debug_ads_free_flag"(), + ) } 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..6140698857 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 @@ -1,14 +1,14 @@ 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.definingClass +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.typeReference +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.parameterTypes import app.revanced.patcher.patch.bytecodePatch -import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.patcher.returnType import app.revanced.util.returnEarly -import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.reference.TypeReference @Suppress("unused") val hideAdsPatch = bytecodePatch( @@ -17,25 +17,16 @@ val hideAdsPatch = bytecodePatch( ) { compatibleWith("com.viber.voip"("25.9.2.0", "26.1.2.0")) - execute { - val method = findAdStringFingerprint.method - - // Find the ads free string index - val stringIndex = findAdStringFingerprint.stringMatches!!.first().index + apply { + val referenceIndex = findAdStringMethodMatch[0] - // Search backwards from the string to find the `new-instance` (TypeReference) instruction - val typeRefIndex = method.indexOfFirstInstructionReversedOrThrow(stringIndex) { this.opcode == Opcode.NEW_INSTANCE } + val targetClass = + findAdStringMethodMatch.immutableMethod.getInstruction(referenceIndex).typeReference - // Get the class name from the TypeReference - val targetClass = method.getInstruction(typeRefIndex).reference as TypeReference - - // Patch the ads-free method to always return true - fingerprint { - returns("I") - parameters() - custom { method, classDef -> - classDef == targetClass - } - }.method.returnEarly(1) + val adFreeMethod = firstMethodDeclaratively { + definingClass(targetClass!!.type) + returnType("I") + parameterTypes() + }.returnEarly(1) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/Fingerprints.kt index 2f2a7bda3d..ab28accbf1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/viber/misc/navbar/Fingerprints.kt @@ -1,16 +1,16 @@ package app.revanced.patches.viber.misc.navbar -import app.revanced.patcher.fingerprint + +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.gettingFirstImmutableMethod +import app.revanced.patcher.parameterTypes import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val tabIdClassFingerprint = fingerprint { - strings("shouldShowTabId") -} +internal val BytecodePatchContext.tabIdClassMethod by gettingFirstImmutableMethod("shouldShowTabId") -context(BytecodePatchContext) -internal val shouldShowTabIdMethodFingerprint get() = fingerprint { - parameters("I", "I") - returns("Z") - custom { methodDef, classDef -> - classDef == tabIdClassFingerprint.classDef - } +context(_: BytecodePatchContext) +internal fun ClassDef.getShouldShowTabIdMethod() = firstMethodDeclaratively { + parameterTypes("I", "I") + returnType("Z") } 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..b828c1f216 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 @@ -1,36 +1,34 @@ package app.revanced.patches.viber.misc.navbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.immutableClassDef 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( - name = PATCH_NAME_HIDE_NAVIGATION_BUTTONS, + name = "Hide navigation buttons", description = "Permanently hides navigation bar buttons, such as Explore and Marketplace.", - use = false + use = false, ) { compatibleWith("com.viber.voip") val hideOptions = AllowedNavigationItems.entries.associateWith { booleanOption( - key = it.key, + name = it.optionName, default = it.defaultHideOption, - title = it.title, description = it.description, ) } - execute { + apply { // Items that won't be forcefully hidden. val allowedItems = hideOptions.filter { (option, enabled) -> enabled.value != true } if (allowedItems.size == AllowedNavigationItems.entries.size) { - return@execute Logger.getLogger(this::class.java.name).warning( - "No hide navigation buttons options are enabled. No changes applied." + return@apply Logger.getLogger(this::class.java.name).warning( + "No hide navigation buttons options are enabled. No changes applied.", ) } @@ -46,8 +44,7 @@ val hideNavigationButtonsPatch = bytecodePatch( nop """ - shouldShowTabIdMethodFingerprint - .method + tabIdClassMethod.immutableClassDef.getShouldShowTabIdMethod() .addInstructionsWithLabels(0, injectionInstructions) } } @@ -59,7 +56,7 @@ val hideNavigationButtonsPatch = bytecodePatch( private enum class AllowedNavigationItems( val defaultHideOption: Boolean, private val itemName: String, - private vararg val ids: Int + private vararg val ids: Int, ) { CHATS(false, "Chats", 0), CALLS(false, "Calls", 1, 7), @@ -67,17 +64,16 @@ private enum class AllowedNavigationItems( MORE(false, "More", 3), PAY(true, "Pay", 5), CAMERA(true, "Camera", 6), - MARKETPLACE(true, "Marketplace", 8); + MARKETPLACE(true, "Marketplace", 8), + ; - val key = "hide$itemName" - val title = "Hide $itemName" + val optionName = "Hide $itemName" val description = "Permanently hides the $itemName button." - fun buildAllowInstruction(): String = - ids.joinToString("\n") { id -> - """ + fun buildAllowInstruction(): String = ids.joinToString("\n") { id -> + """ const/4 v0, $id # If tabId == $id ($itemName), don't hide it if-eq p1, v0, :continue """ - } + } } 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/warnwetter/misc/firebasegetcert/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/Fingerprints.kt index 6eb7bd176b..0c35eec719 100644 --- a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/Fingerprints.kt @@ -1,21 +1,21 @@ package app.revanced.patches.warnwetter.misc.firebasegetcert -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val getMessagingCertFingerprint = fingerprint { - returns("Ljava/lang/String;") - strings( - "ContentValues", - "Could not get fingerprint hash for package: ", - "No such package: ", - ) +internal val BytecodePatchContext.getMessagingCertMethod by gettingFirstMethodDeclaratively( + "ContentValues", + "Could not get fingerprint hash for package: ", + "No such package: ", +) { + returnType("Ljava/lang/String;") } -internal val getRegistrationCertFingerprint = fingerprint { - returns("Ljava/lang/String;") - strings( - "FirebaseRemoteConfig", - "Could not get fingerprint hash for package: ", - "No such package: ", - ) +internal val BytecodePatchContext.getRegistrationCertMethod by gettingFirstMethodDeclaratively( + "FirebaseRemoteConfig", + "Could not get fingerprint hash for package: ", + "No such package: ", +) { + returnType("Ljava/lang/String;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt index 416ccc8f55..7fed6ff70a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt @@ -8,9 +8,9 @@ val firebaseGetCertPatch = bytecodePatch( ) { compatibleWith("de.dwd.warnapp") - execute { - listOf(getRegistrationCertFingerprint, getMessagingCertFingerprint).forEach { match -> - match.method.returnEarly("0799DDF0414D3B3475E88743C91C0676793ED450") + apply { + listOf(getRegistrationCertMethod, getMessagingCertMethod).forEach { method -> + method.returnEarly("0799DDF0414D3B3475E88743C91C0676793ED450") } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt index d33880de70..1489cf9658 100644 --- a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt @@ -1,9 +1,11 @@ package app.revanced.patches.warnwetter.misc.promocode -import app.revanced.patcher.fingerprint +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.patch.BytecodePatchContext -internal val promoCodeUnlockFingerprint = fingerprint { - custom { method, classDef -> - classDef.endsWith("PromoTokenVerification;") && method.name == "isValid" - } +internal val BytecodePatchContext.promoCodeUnlockMethod by gettingFirstMethodDeclaratively { + name("isValid") + definingClass("PromoTokenVerification;") } diff --git a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt index 8acb8a650b..af3e587954 100644 --- a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt @@ -1,8 +1,8 @@ package app.revanced.patches.warnwetter.misc.promocode -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.warnwetter.misc.firebasegetcert.firebaseGetCertPatch +import app.revanced.util.returnEarly @Suppress("unused") val promoCodeUnlockPatch = bytecodePatch( @@ -13,13 +13,7 @@ val promoCodeUnlockPatch = bytecodePatch( compatibleWith("de.dwd.warnapp"("4.2.2")) - execute { - promoCodeUnlockFingerprint.method.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) + apply { + promoCodeUnlockMethod.returnEarly(true) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt index e326c26825..9af8ef42ac 100644 --- a/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt @@ -1,26 +1,23 @@ package app.revanced.patches.willhaben.ads -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val adResolverFingerprint = fingerprint { +internal val BytecodePatchContext.adResolverMethod by gettingFirstMethodDeclaratively( + "Google Ad is invalid ", + "Google Native Ad is invalid ", + "Criteo Ad is invalid ", + "Amazon Ad is invalid ", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters("L", "L") - strings( - "Google Ad is invalid ", - "Google Native Ad is invalid ", - "Criteo Ad is invalid ", - "Amazon Ad is invalid ", - ) + returnType("L") + parameterTypes("L", "L") } -internal val whAdViewInjectorFingerprint = fingerprint { +internal val BytecodePatchContext.whAdViewInjectorMethod by gettingFirstMethodDeclaratively("successfulAdView") { + definingClass("Lat/willhaben/advertising/WHAdView;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L", "L", "L", "Z") - strings("successfulAdView") - custom { _, classDef -> - classDef.type == "Lat/willhaben/advertising/WHAdView;" - } + returnType("V") + parameterTypes("L", "L", "L", "Z") } 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..3f3c36699c 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 @@ -4,14 +4,14 @@ import app.revanced.patcher.patch.bytecodePatch import app.revanced.util.returnEarly @Suppress("unused") -internal val hideAdsPatch = bytecodePatch( +val hideAdsPatch = bytecodePatch( name = "Hide ads", description = "Hides all in-app ads.", ) { compatibleWith("at.willhaben") - execute { - adResolverFingerprint.method.returnEarly() - whAdViewInjectorFingerprint.method.returnEarly() + apply { + adResolverMethod.returnEarly() + whAdViewInjectorMethod.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/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/Fingerprints.kt index 254b440f39..d1fa2a873b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/Fingerprints.kt @@ -1,6 +1,7 @@ package app.revanced.patches.youtube.ad.general -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.containsLiteralInstruction import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionReversed @@ -8,18 +9,16 @@ import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.reference.MethodReference -internal val fullScreenEngagementAdContainerFingerprint = fingerprint { +internal val BytecodePatchContext.fullScreenEngagementAdContainerMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - custom { method, _ -> - method.containsLiteralInstruction(fullScreenEngagementAdContainer) - && indexOfAddListInstruction(method) >= 0 + returnType("V") + parameterTypes() + custom { + containsLiteralInstruction(fullScreenEngagementAdContainer) && + indexOfAddListInstruction(this) >= 0 } } -internal fun indexOfAddListInstruction(method: Method) = - method.indexOfFirstInstructionReversed { - getReference()?.name == "add" - } - +internal fun indexOfAddListInstruction(method: Method) = method.indexOfFirstInstructionReversed { + getReference()?.name == "add" +} 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 436a5d1d05..fb9b6756bf 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 @@ -1,35 +1,35 @@ 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.extensions.getInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.extensions.wideLiteral +import app.revanced.patcher.firstMethod 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.litho.filter.addLithoFilter -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType 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.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.util.findMutableMethodOf +import app.revanced.util.forEachInstructionAsSequence import app.revanced.util.injectHideViewCall 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 internal var adAttributionId = -1L private set internal var fullScreenEngagementAdContainer = -1L private set -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/AdsFilter;" +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/litho/AdsFilter;" private val hideAdsResourcePatch = resourcePatch { dependsOn( @@ -39,7 +39,7 @@ private val hideAdsResourcePatch = resourcePatch { addResourcesPatch, ) - execute { + apply { addResources("youtube", "ad.general.hideAdsResourcePatch") PreferenceScreen.ADS.addPreferences( @@ -55,10 +55,10 @@ private val hideAdsResourcePatch = resourcePatch { SwitchPreference("revanced_hide_web_search_results"), ) - addLithoFilter("Lapp/revanced/extension/youtube/patches/components/AdsFilter;") + addLithoFilter("Lapp/revanced/extension/youtube/patches/litho/AdsFilter;") - adAttributionId = resourceMappings["id", "ad_attribution"] - fullScreenEngagementAdContainer = resourceMappings["id", "fullscreen_engagement_ad_container"] + adAttributionId = ResourceType.ID["ad_attribution"] + fullScreenEngagementAdContainer = ResourceType.ID["fullscreen_engagement_ad_container"] } } @@ -76,17 +76,17 @@ 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", + ), ) - execute { - // Hide end screen store banner + apply { + // Hide end screen store banner. - fullScreenEngagementAdContainerFingerprint.method.apply { + fullScreenEngagementAdContainerMethod.apply { val addListIndex = indexOfAddListInstruction(this) val addListInstruction = getInstruction(addListIndex) val listRegister = addListInstruction.registerC @@ -95,47 +95,26 @@ val hideAdsPatch = bytecodePatch( replaceInstruction( addListIndex, "invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" + - "->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V" + "->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V", ) } - // Hide ad views + // Hide ad views. - classes.forEach { classDef -> - classDef.methods.forEach { method -> - with(method.implementation) { - this?.instructions?.forEachIndexed { index, instruction -> - if (instruction.opcode != Opcode.CONST) { - return@forEachIndexed - } - // Instruction to store the id adAttribution into a register - if ((instruction as Instruction31i).wideLiteral != adAttributionId) { - return@forEachIndexed - } + forEachInstructionAsSequence({ _, method, instruction, index -> + if (instruction.opcode != Opcode.CONST) return@forEachInstructionAsSequence null + if (instruction.wideLiteral != adAttributionId) return@forEachInstructionAsSequence null - val insertIndex = index + 1 + val insertIndex = index + 1 - // Call to get the view with the id adAttribution - with(instructions.elementAt(insertIndex)) { - if (opcode != Opcode.INVOKE_VIRTUAL) { - return@forEachIndexed - } + // Call to get the view with the id adAttribution. + if (method.instructions.elementAt(insertIndex).opcode != Opcode.INVOKE_VIRTUAL) return@forEachInstructionAsSequence null + val viewRegister = method.getInstruction(insertIndex).registerC - // Hide the view - val viewRegister = (this as Instruction35c).registerC - proxy(classDef) - .mutableClass - .findMutableMethodOf(method) - .injectHideViewCall( - insertIndex, - viewRegister, - EXTENSION_CLASS_DESCRIPTOR, - "hideAdAttributionView", - ) - } - } - } - } + return@forEachInstructionAsSequence insertIndex to viewRegister + + }) { method, (insertIndex, viewRegister) -> + method.injectHideViewCall(insertIndex, viewRegister, EXTENSION_CLASS_DESCRIPTOR, "hideAdAttributionView") } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt index 7629d1760d..b8ce3bbb24 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt @@ -1,21 +1,20 @@ package app.revanced.patches.youtube.ad.getpremium -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val getPremiumViewFingerprint = fingerprint { +internal val BytecodePatchContext.getPremiumViewMethodMatch by composingFirstMethod { + name("onMeasure") + definingClass("Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;") accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) - returns("V") - parameters("I", "I") + returnType("V") + parameterTypes("I", "I") opcodes( Opcode.ADD_INT_2ADDR, Opcode.ADD_INT_2ADDR, Opcode.INVOKE_VIRTUAL, Opcode.RETURN_VOID, ) - custom { method, _ -> - method.name == "onMeasure" && - method.definingClass == "Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;" - } } 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..3b19542202 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 @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.ad.getpremium -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -25,29 +25,29 @@ 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", + ), ) - execute { + apply { addResources("youtube", "ad.getpremium.hideGetPremiumPatch") PreferenceScreen.ADS.addPreferences( SwitchPreference("revanced_hide_get_premium"), ) - getPremiumViewFingerprint.method.apply { - val startIndex = getPremiumViewFingerprint.patternMatch!!.startIndex - val measuredWidthRegister = getInstruction(startIndex).registerA - val measuredHeightInstruction = getInstruction(startIndex + 1) + getPremiumViewMethodMatch.let { + val startIndex = it[0] + val measuredWidthRegister = it.method.getInstruction(startIndex).registerA + val measuredHeightInstruction = it.method.getInstruction(startIndex + 1) val measuredHeightRegister = measuredHeightInstruction.registerA val tempRegister = measuredHeightInstruction.registerB - addInstructionsWithLabels( + it.method.addInstructionsWithLabels( startIndex + 2, """ # Override the internal measurement of the layout with zero values. 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..2565c0e79f 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 @@ -1,11 +1,9 @@ package app.revanced.patches.youtube.ad.video -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext -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: ", - ) -} +internal val BytecodePatchContext.loadVideoAdsMethod by gettingFirstMethodDeclaratively( + "TriggerBundle doesn't have the required metadata specified by the trigger ", + "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..5ad8b5da75 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 @@ -1,9 +1,9 @@ package app.revanced.patches.youtube.ad.video -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference @@ -11,6 +11,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch +@Suppress("ObjectPropertyName") val videoAdsPatch = bytecodePatch( name = "Video ads", description = "Adds an option to remove ads in the video player.", @@ -23,21 +24,21 @@ 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", + ), ) - execute { + apply { addResources("youtube", "ad.video.videoAdsPatch") PreferenceScreen.ADS.addPreferences( SwitchPreference("revanced_hide_video_ads"), ) - loadVideoAdsFingerprint.method.addInstructionsWithLabels( + loadVideoAdsMethod.addInstructionsWithLabels( 0, """ invoke-static { }, Lapp/revanced/extension/youtube/patches/VideoAdsPatch;->shouldShowAds()Z @@ -45,7 +46,7 @@ val videoAdsPatch = bytecodePatch( if-nez v0, :show_video_ads return-void """, - ExternalLabel("show_video_ads", loadVideoAdsFingerprint.method.getInstruction(0)), + ExternalLabel("show_video_ads", loadVideoAdsMethod.getInstruction(0)), ) } } 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..18656fa5c4 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,11 +15,11 @@ import app.revanced.util.copyResources private val copyVideoUrlResourcePatch = resourcePatch { dependsOn( settingsPatch, - playerControlsResourcePatch, + playerControlsPatch, addResourcesPatch, ) - execute { + apply { addResources("youtube", "interaction.copyvideourl.copyVideoUrlResourcePatch") PreferenceScreen.PLAYER.addPreferences( @@ -41,7 +41,7 @@ private val copyVideoUrlResourcePatch = resourcePatch { } @Suppress("unused") -val copyVideoUrlPatch = bytecodePatch( +val copyVideoURLPatch = bytecodePatch( name = "Copy video URL", description = "Adds options to display buttons in the video player to copy video URLs.", ) { @@ -53,14 +53,14 @@ 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", + ), ) - execute { + apply { val extensionPlayerPackage = "Lapp/revanced/extension/youtube/videoplayer" val buttonsDescriptors = listOf( "$extensionPlayerPackage/CopyVideoUrlButton;", 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..fa5c83ebe5 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.* +import app.revanced.patcher.patch.BytecodePatchContext -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() +internal val BytecodePatchContext.createDialogMethodMatch by composingFirstMethod { + returnType("V") + parameterTypes("L", "L", "Ljava/lang/String;") + instructions( + method { toString() == $$"Landroid/app/AlertDialog$Builder;->setNegativeButton(ILandroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;" }, + method { toString() == $$"Landroid/app/AlertDialog$Builder;->setOnCancelListener(Landroid/content/DialogInterface$OnCancelListener;)Landroid/app/AlertDialog$Builder;" }, + method { toString() == $$"Landroid/app/AlertDialog$Builder;->create()Landroid/app/AlertDialog;" }, + method { toString() == "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..c0d7e173e3 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 @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.interaction.dialog -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -11,6 +11,10 @@ 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;" + +@Suppress("unused") 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,32 +28,30 @@ 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 { + apply { addResources("youtube", "interaction.dialog.removeViewerDiscretionDialogPatch") PreferenceScreen.GENERAL_LAYOUT.addPreferences( SwitchPreference("revanced_remove_viewer_discretion_dialog"), ) - createDialogFingerprint.method.apply { - val showDialogIndex = implementation!!.instructions.lastIndex - 2 - val dialogRegister = getInstruction(showDialogIndex).registerC + createDialogMethodMatch.let { + it.method.apply { + val showDialogIndex = it[-1] + 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..adfbd1cdf9 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 @@ -1,16 +1,19 @@ package app.revanced.patches.youtube.interaction.doubletap -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.extensions.addInstructions 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_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,27 +27,37 @@ 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 { + apply { + 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@apply Logger.getLogger(this::class.java.name).warning( + "Disable double tap actions requires 20.14.43+", + ) + } + addResources("youtube", "interaction.doubletap.disableDoubleTapActionsPatch") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_disable_chapter_skip_double_tap"), ) - val doubleTapInfoGetSeekSourceFingerprint = fingerprint { + val doubleTapInfoGetSeekSourceMethod = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Z") - returns(seekTypeEnumFingerprint.originalClassDef.type) + parameterTypes("Z") + returnType(seekTypeEnumMethod.immutableClassDef.type) opcodes( Opcode.IF_EQZ, Opcode.SGET_OBJECT, @@ -52,33 +65,24 @@ val disableDoubleTapActionsPatch = bytecodePatch( Opcode.SGET_OBJECT, Opcode.RETURN_OBJECT, ) - custom { _, classDef -> - classDef.fields.count() == 4 - } + custom { immutableClassDef.fields.count() == 4 } } // Force isChapterSeek flag to false. - doubleTapInfoGetSeekSourceFingerprint.method.addInstructions( + doubleTapInfoGetSeekSourceMethod.addInstructions( 0, """ invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->disableDoubleTapChapters(Z)Z move-result p1 - """ + """, ) - doubleTapInfoCtorFingerprint.match( - doubleTapInfoGetSeekSourceFingerprint.classDef - ).method.addInstructions( + doubleTapInfoGetSeekSourceMethod.immutableClassDef.getDoubleTapInfoCtorMethod().addInstructions( 0, """ invoke-static { p3 }, $EXTENSION_CLASS_DESCRIPTOR->disableDoubleTapChapters(Z)Z move-result p3 - """ + """, ) } } - -@Deprecated("Patch was renamed", ReplaceWith("disableDoubleTapActionsPatch")) -val disableChapterSkipDoubleTapPatch = bytecodePatch { - dependsOn(disableDoubleTapActionsPatch) -} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/Fingerprints.kt index 4524a68534..d65348e442 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/doubletap/Fingerprints.kt @@ -1,22 +1,27 @@ package app.revanced.patches.youtube.interaction.doubletap -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val seekTypeEnumFingerprint = fingerprint { +internal val BytecodePatchContext.seekTypeEnumMethod by gettingFirstImmutableMethodDeclaratively( + "SEEK_SOURCE_SEEK_TO_NEXT_CHAPTER", + "SEEK_SOURCE_SEEK_TO_PREVIOUS_CHAPTER", +) { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - strings( - "SEEK_SOURCE_SEEK_TO_NEXT_CHAPTER", - "SEEK_SOURCE_SEEK_TO_PREVIOUS_CHAPTER" - ) } -internal val doubleTapInfoCtorFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getDoubleTapInfoCtorMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters( + parameterTypes( "Landroid/view/MotionEvent;", "I", "Z", - "Lj\$/time/Duration;" + "Lj$/time/Duration;", ) } 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..c1d1cdcbc4 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 @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.interaction.downloads -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources @@ -14,22 +14,21 @@ 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 +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.util.ResourceGroup import app.revanced.util.copyResources private val downloadsResourcePatch = resourcePatch { dependsOn( - playerControlsResourcePatch, + playerControlsPatch, settingsPatch, addResourcesPatch, ) - execute { + apply { addResources("youtube", "interaction.downloads.downloadsResourcePatch") PreferenceScreen.PLAYER.addPreferences( @@ -74,24 +73,24 @@ 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", + ), ) - execute { + apply { initializeBottomControl(BUTTON_DESCRIPTOR) injectVisibilityCheckCall(BUTTON_DESCRIPTOR) // Main activity is used to launch downloader intent. - mainActivityOnCreateFingerprint.method.addInstruction( + mainActivityOnCreateMethod.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 { + offlineVideoEndpointMethod.apply { addInstructionsWithLabels( 0, """ 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..1eac7848a8 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,16 +1,19 @@ package app.revanced.patches.youtube.interaction.downloads +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -internal val offlineVideoEndpointFingerprint = fingerprint { +internal val BytecodePatchContext.offlineVideoEndpointMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters( + returnType("V") + parameterTypes( "Ljava/util/Map;", "L", "Ljava/lang/String", // VideoId "L", ) - strings("Object is not an offlineable video: ") + instructions( + "Object is not an offlineable video: "(), + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt index 8cecc1400a..18af02c1e2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt @@ -1,9 +1,10 @@ package app.revanced.patches.youtube.interaction.seekbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference @@ -23,16 +24,14 @@ val disablePreciseSeekingGesturePatch = bytecodePatch( addResourcesPatch, ) - execute { + apply { addResources("youtube", "interaction.seekbar.disablePreciseSeekingGesturePatch") PreferenceScreen.SEEKBAR.addPreferences( SwitchPreference("revanced_disable_precise_seeking_gesture"), ) - allowSwipingUpGestureFingerprint.match( - swipingUpGestureParentFingerprint.originalClassDef, - ).method.apply { + swipingUpGestureParentMethod.immutableClassDef.getAllowSwipingUpGestureMethod().apply { addInstructionsWithLabels( 0, """ @@ -45,9 +44,7 @@ val disablePreciseSeekingGesturePatch = bytecodePatch( ) } - showSwipingUpGuideFingerprint.match( - swipingUpGestureParentFingerprint.originalClassDef, - ).method.apply { + swipingUpGestureParentMethod.immutableClassDef.getShowSwipingUpGuideMethod().apply { addInstructionsWithLabels( 0, """ 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..c3e867c496 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 @@ -1,9 +1,9 @@ package app.revanced.patches.youtube.interaction.seekbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference @@ -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 @@ -28,7 +26,7 @@ val enableSeekbarTappingPatch = bytecodePatch( addResourcesPatch, ) - execute { + apply { addResources("youtube", "interaction.seekbar.enableSeekbarTappingPatch") PreferenceScreen.SEEKBAR.addPreferences( @@ -36,39 +34,49 @@ val enableSeekbarTappingPatch = bytecodePatch( ) // Find the required methods to tap the seekbar. - val seekbarTappingMethods = onTouchEventHandlerFingerprint.let { - fun getMethodReference(index: Int) = it.method.getInstruction(index) + val seekbarTappingMethods = onTouchEventHandlerMethodMatch.let { + fun getReference(index: Int) = it.method.getInstruction(index) .reference as MethodReference listOf( - getMethodReference(it.patternMatch!!.startIndex), - getMethodReference(it.patternMatch!!.endIndex) + getReference(it[0]), + getReference(it[-1]), ) } - seekbarTappingFingerprint.method.apply { - val pointIndex = indexOfNewPointInstruction(this) - val invokeIndex = indexOfFirstInstructionOrThrow(pointIndex, Opcode.INVOKE_VIRTUAL) - val insertIndex = invokeIndex + 1 + seekbarTappingMethodMatch.let { + val insertIndex = it[-1] + 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[2], + ).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..ceefafbb94 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 @@ -1,7 +1,8 @@ package app.revanced.patches.youtube.interaction.seekbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.methodReference import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources @@ -22,7 +23,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/ val enableSlideToSeekPatch = bytecodePatch( description = "Adds an option to enable slide to seek " + - "instead of playing at 2x speed when pressing and holding in the video player." + "instead of playing at 2x speed when pressing and holding in the video player.", ) { dependsOn( sharedExtensionPatch, @@ -31,7 +32,7 @@ val enableSlideToSeekPatch = bytecodePatch( versionCheckPatch, ) - execute { + apply { addResources("youtube", "interaction.seekbar.enableSlideToSeekPatch") PreferenceScreen.SEEKBAR.addPreferences( @@ -42,16 +43,16 @@ val enableSlideToSeekPatch = bytecodePatch( // Restore the behaviour to slide to seek. - val checkIndex = slideToSeekFingerprint.patternMatch!!.startIndex - val checkReference = slideToSeekFingerprint.method.getInstruction(checkIndex) + val checkIndex = slideToSeekMethodMatch[0] + val checkReference = slideToSeekMethodMatch.method.getInstruction(checkIndex) .getReference()!! val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->isSlideToSeekDisabled(Z)Z" // A/B check method was only called on this class. - slideToSeekFingerprint.classDef.methods.forEach { method -> + slideToSeekMethodMatch.classDef.methods.forEach { method -> method.findInstructionIndicesReversed { - opcode == Opcode.INVOKE_VIRTUAL && getReference() == checkReference + opcode == Opcode.INVOKE_VIRTUAL && methodReference == checkReference }.forEach { index -> method.apply { val register = getInstruction(index + 1).registerA @@ -73,9 +74,9 @@ val enableSlideToSeekPatch = bytecodePatch( // Disable the double speed seek gesture. if (is_19_17_or_greater) { - disableFastForwardGestureFingerprint.let { + disableFastForwardGestureMethodMatch.let { it.method.apply { - val targetIndex = it.patternMatch!!.endIndex + val targetIndex = it[-1] val targetRegister = getInstruction(targetIndex).registerA addInstructions( @@ -88,17 +89,19 @@ val enableSlideToSeekPatch = bytecodePatch( } } } else { - disableFastForwardLegacyFingerprint.method.apply { - val insertIndex = disableFastForwardLegacyFingerprint.patternMatch!!.endIndex + 1 - val targetRegister = getInstruction(insertIndex).registerA + disableFastForwardLegacyMethodMatch.let { + it.method.apply { + val insertIndex = it[-1] + 1 + val targetRegister = getInstruction(insertIndex).registerA - addInstructions( - insertIndex, - """ - invoke-static { v$targetRegister }, $extensionMethodDescriptor - move-result v$targetRegister - """, - ) + 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..38683f9acc 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,66 +1,90 @@ package app.revanced.patches.youtube.interaction.seekbar -import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionReversed +import app.revanced.patcher.* +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.stringReference +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.youtube.misc.playservice.* +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.ClassDef -internal val swipingUpGestureParentFingerprint = fingerprint { - returns("Z") - parameters() - literal { 45379021 } +internal val BytecodePatchContext.swipingUpGestureParentMethod by gettingFirstImmutableMethodDeclaratively { + returnType("Z") + parameterTypes() + instructions( + 45379021L(), // Swipe up fullscreen feature flag + ) } /** - * Resolves using the class found in [swipingUpGestureParentFingerprint]. + * Resolves using the class found in [swipingUpGestureParentMethod]. */ -internal val showSwipingUpGuideFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getShowSwipingUpGuideMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.FINAL) - returns("Z") - parameters() - literal { 1 } + returnType("Z") + parameterTypes() + instructions(1L()) } /** - * Resolves using the class found in [swipingUpGestureParentFingerprint]. + * Resolves using the class found in [swipingUpGestureParentMethod]. */ -internal val allowSwipingUpGestureFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getAllowSwipingUpGestureMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") + returnType("V") + parameterTypes("L") } -internal val disableFastForwardLegacyFingerprint = fingerprint { - returns("Z") - parameters() +internal val BytecodePatchContext.disableFastForwardLegacyMethodMatch by composingFirstMethod { + returnType("Z") + parameterTypes() opcodes(Opcode.MOVE_RESULT) + // Intent start flag only used in the subscription activity literal { 45411330 } } -internal val disableFastForwardGestureFingerprint = fingerprint { +internal val BytecodePatchContext.disableFastForwardGestureMethodMatch by composingFirstMethod { + definingClass("/NextGenWatchLayout;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() + returnType("Z") + parameterTypes() opcodes( Opcode.IF_EQZ, Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, ) - custom { methodDef, classDef -> - methodDef.implementation!!.instructions.count() > 30 && - classDef.type.endsWith("/NextGenWatchLayout;") + custom { instructions.count() > 30 } +} + +internal val BytecodePatchContext.customTapAndHoldMethodMatch by composingFirstMethod { + name("run") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes() + instructions(2.0f.toRawBits().toLong()()) + custom { + // 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 + + indexOfFirstInstruction { + val string = stringReference?.string + string == "Failed to easy seek haptics vibrate." || + (findSearchLandingKey && string == "search_landing_cache_key") + } >= 0 } } -internal val onTouchEventHandlerFingerprint = fingerprint { +internal val BytecodePatchContext.onTouchEventHandlerMethodMatch by composingFirstMethod { + name("onTouchEvent") accessFlags(AccessFlags.PUBLIC, AccessFlags.PUBLIC) - returns("Z") - parameters("L") + returnType("Z") + parameterTypes("L") opcodes( Opcode.INVOKE_VIRTUAL, // nMethodReference Opcode.RETURN, @@ -77,30 +101,28 @@ internal val onTouchEventHandlerFingerprint = fingerprint { Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL, // oMethodReference ) - custom { method, _ -> method.name == "onTouchEvent" } } -internal val seekbarTappingFingerprint = fingerprint { +internal val BytecodePatchContext.seekbarTappingMethodMatch by composingFirstMethod { + name("onTouchEvent") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters("L") - custom { method, _ -> - method.name == "onTouchEvent" - && method.containsLiteralInstruction(Integer.MAX_VALUE.toLong()) - && indexOfNewPointInstruction(method) >= 0 - } + returnType("Z") + parameterTypes("Landroid/view/MotionEvent;") + instructions( + Int.MAX_VALUE.toLong()(), + allOf(Opcode.NEW_INSTANCE(), type("Landroid/graphics/Point;")), + after(method { toString() == "Landroid/graphics/Point;->(II)V" }), + after(method { toString() == "Lj$/util/Optional;->of(Ljava/lang/Object;)Lj$/util/Optional;" }), + after(Opcode.MOVE_RESULT_OBJECT()), + after(allOf(Opcode.IPUT_OBJECT(), field { type == "Lj$/util/Optional;" })), + afterAtMost(10, Opcode.INVOKE_VIRTUAL()), + ) } -internal fun indexOfNewPointInstruction(method: Method) = method.indexOfFirstInstructionReversed { - val reference = getReference() - reference?.definingClass == "Landroid/graphics/Point;" - && reference.name == "" -} - -internal val slideToSeekFingerprint = fingerprint { +internal val BytecodePatchContext.slideToSeekMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") - parameters("Landroid/view/View;", "F") + returnType("V") + parameterTypes("Landroid/view/View;", "F") opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, @@ -110,9 +132,18 @@ internal val slideToSeekFingerprint = fingerprint { literal { 67108864 } } -internal val fullscreenSeekbarThumbnailsQualityFingerprint = fingerprint { +internal val BytecodePatchContext.fullscreenSeekbarThumbnailsQualityMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - literal { 45399684L } + returnType("Z") + parameterTypes() + instructions( + 45399684L(), // Video stream seekbar thumbnails feature flag. + ) +} + +internal val BytecodePatchContext.fullscreenLargeSeekbarFeatureFlagMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Z") + parameterTypes() + instructions(45691569L()) } 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..185ef9385c 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 @@ -1,16 +1,22 @@ package app.revanced.patches.youtube.interaction.seekbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.immutableClassDef 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.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.patches.youtube.shared.seekbarMethod +import app.revanced.patches.youtube.shared.getSeekbarOnDrawMethodMatch +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,21 +26,23 @@ val hideSeekbarPatch = bytecodePatch( settingsPatch, seekbarColorPatch, addResourcesPatch, + versionCheckPatch, ) - execute { + apply { addResources("youtube", "layout.hide.seekbar.hideSeekbarPatch") PreferenceScreen.SEEKBAR.addPreferences( SwitchPreference("revanced_hide_seekbar"), SwitchPreference("revanced_hide_seekbar_thumbnail"), + SwitchPreference("revanced_fullscreen_large_seekbar"), ) - seekbarOnDrawFingerprint.match(seekbarFingerprint.originalClassDef).method.addInstructionsWithLabels( + seekbarMethod.immutableClassDef.getSeekbarOnDrawMethodMatch().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 @@ -42,5 +50,14 @@ val hideSeekbarPatch = bytecodePatch( nop """, ) + + if (is_20_28_or_greater) { + fullscreenLargeSeekbarFeatureFlagMethodMatch.let { + it.method.insertLiteralOverride( + it[0], + "$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..6033875241 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 @@ -6,24 +6,24 @@ import app.revanced.patcher.patch.bytecodePatch val seekbarPatch = bytecodePatch( name = "Seekbar", description = "Adds options to disable precise seeking when swiping up on the seekbar, " + - "slide to seek instead of playing at 2x speed when pressing and holding, " + - "tapping the player seekbar to seek, " + - "and hiding the video player seekbar." + "slide to seek instead of playing at 2x speed when pressing and holding, " + + "tapping the player seekbar to seek, " + + "and hiding the video player seekbar.", ) { dependsOn( disablePreciseSeekingGesturePatch, enableSlideToSeekPatch, enableSeekbarTappingPatch, hideSeekbarPatch, - seekbarThumbnailsPatch + seekbarThumbnailsPatch, ) 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..2ffae0d6b9 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 @@ -1,13 +1,13 @@ package app.revanced.patches.youtube.interaction.seekbar -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instructions 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.layout.seekbar.fullscreenSeekbarThumbnailsFingerprint +import app.revanced.patches.youtube.layout.seekbar.fullscreenSeekbarThumbnailsMethod 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_20_09_or_greater @@ -18,7 +18,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SeekbarThumbnailsPatch;" val seekbarThumbnailsPatch = bytecodePatch( - description = "Adds an option to use high quality fullscreen seekbar thumbnails." + description = "Adds an option to use high quality fullscreen seekbar thumbnails.", ) { dependsOn( sharedExtensionPatch, @@ -26,18 +26,18 @@ val seekbarThumbnailsPatch = bytecodePatch( versionCheckPatch, ) - execute { + apply { if (is_20_09_or_greater) { // High quality seekbar thumbnails is partially broken in 20.09 // and the code is completely removed in 20.10+ - return@execute + return@apply } addResources("youtube", "layout.seekbar.seekbarThumbnailsPatch") if (is_19_17_or_greater) { PreferenceScreen.SEEKBAR.addPreferences( - SwitchPreference("revanced_seekbar_thumbnails_high_quality") + SwitchPreference("revanced_seekbar_thumbnails_high_quality"), ) } else { PreferenceScreen.SEEKBAR.addPreferences( @@ -45,11 +45,11 @@ val seekbarThumbnailsPatch = bytecodePatch( SwitchPreference( key = "revanced_seekbar_thumbnails_high_quality", summaryOnKey = "revanced_seekbar_thumbnails_high_quality_legacy_summary_on", - summaryOffKey = "revanced_seekbar_thumbnails_high_quality_legacy_summary_on" - ) + summaryOffKey = "revanced_seekbar_thumbnails_high_quality_legacy_summary_on", + ), ) - fullscreenSeekbarThumbnailsFingerprint.method.apply { + fullscreenSeekbarThumbnailsMethod.apply { val moveResultIndex = instructions.lastIndex - 1 addInstruction( @@ -59,13 +59,13 @@ val seekbarThumbnailsPatch = bytecodePatch( } } - fullscreenSeekbarThumbnailsQualityFingerprint.method.addInstructions( + fullscreenSeekbarThumbnailsQualityMethod.addInstructions( 0, """ invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->useHighQualityFullscreenThumbnails()Z move-result v0 return v0 - """ + """, ) } } 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..91a1e9af1e 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,23 +1,24 @@ package app.revanced.patches.youtube.interaction.swipecontrols -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.accessFlags +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.definingClass +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val swipeControlsHostActivityFingerprint = fingerprint { +internal val BytecodePatchContext.swipeControlsHostActivityMethod by gettingFirstImmutableMethodDeclaratively { + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters() - custom { method, _ -> - method.definingClass == EXTENSION_CLASS_DESCRIPTOR - } + parameterTypes() } -internal const val SWIPE_CHANGE_VIDEO_FEATURE_FLAG = 45631116L - -internal val swipeChangeVideoFingerprint = fingerprint { +internal val BytecodePatchContext.swipeChangeVideoMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("L") - literal { - SWIPE_CHANGE_VIDEO_FEATURE_FLAG - } + instructions( + 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..6c49c61618 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 @@ -1,8 +1,9 @@ package app.revanced.patches.youtube.interaction.swipecontrols +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch -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.settings.preference.InputType @@ -12,27 +13,35 @@ 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 +import app.revanced.patches.youtube.shared.mainActivityConstructorMethod import app.revanced.util.* import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.immutable.ImmutableMethod -internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity;" +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity;" private val swipeControlsResourcePatch = resourcePatch { dependsOn( settingsPatch, addResourcesPatch, + versionCheckPatch, ) - execute { + apply { 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") + SwitchPreference("revanced_swipe_change_video"), ) } @@ -45,12 +54,16 @@ private val swipeControlsResourcePatch = resourcePatch { SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"), ListPreference("revanced_swipe_overlay_style"), TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER), - TextPreference("revanced_swipe_overlay_progress_brightness_color", + TextPreference( + "revanced_swipe_overlay_progress_brightness_color", tag = "app.revanced.extension.shared.settings.preference.ColorPickerWithOpacitySliderPreference", - inputType = InputType.TEXT_CAP_CHARACTERS), - TextPreference("revanced_swipe_overlay_progress_volume_color", + inputType = InputType.TEXT_CAP_CHARACTERS, + ), + TextPreference( + "revanced_swipe_overlay_progress_volume_color", tag = "app.revanced.extension.shared.settings.preference.ColorPickerWithOpacitySliderPreference", - inputType = InputType.TEXT_CAP_CHARACTERS), + inputType = InputType.TEXT_CAP_CHARACTERS, + ), TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER), TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER), TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER), @@ -88,16 +101,16 @@ 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", + ), ) - execute { - val wrapperClass = swipeControlsHostActivityFingerprint.classDef - val targetClass = mainActivityConstructorFingerprint.classDef + apply { + val wrapperClass = swipeControlsHostActivityMethod.classDef + val targetClass = mainActivityConstructorMethod.classDef // Inject the wrapper class from the extension into the class hierarchy of MainActivity. wrapperClass.setSuperClass(targetClass.superclass) @@ -122,11 +135,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) { + swipeChangeVideoMethodMatch.let { + it.method.insertLiteralOverride( + it[-1], + "$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..bc67ed397c 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 @@ -1,6 +1,6 @@ package app.revanced.patches.youtube.layout.autocaptions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -12,7 +12,8 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;" -val autoCaptionsPatch = bytecodePatch( +@Suppress("unused") +val disableAutoCaptionsPatch = bytecodePatch( name = "Disable auto captions", description = "Adds an option to disable captions from being automatically enabled.", ) { @@ -24,21 +25,21 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.autocaptions.autoCaptionsPatch") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_disable_auto_captions"), ) - subtitleTrackFingerprint.method.addInstructions( + subtitleTrackMethod.addInstructions( 0, """ invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableAutoCaptions()Z @@ -48,19 +49,19 @@ val autoCaptionsPatch = bytecodePatch( return v0 :auto_captions_enabled nop - """ + """, ) - mapOf( - startVideoInformerFingerprint to 0, - storyboardRendererDecoderRecommendedLevelFingerprint to 1 - ).forEach { (fingerprint, enabled) -> - fingerprint.method.addInstructions( + arrayOf( + startVideoInformerMethod to 0, + storyboardRendererDecoderRecommendedLevelMethod to 1, + ).forEach { (method, enabled) -> + method.addInstructions( 0, """ const/4 v0, 0x$enabled invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setCaptionsButtonStatus(Z)V - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt index c657b72ec2..a85542b891 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt @@ -1,30 +1,30 @@ package app.revanced.patches.youtube.layout.autocaptions -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val startVideoInformerFingerprint = fingerprint { +internal val BytecodePatchContext.startVideoInformerMethod by gettingFirstMethodDeclaratively("pc") { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") + returnType("V") opcodes( Opcode.INVOKE_INTERFACE, Opcode.RETURN_VOID, ) - strings("pc") } -internal val storyboardRendererDecoderRecommendedLevelFingerprint = fingerprint { - returns("V") +internal val BytecodePatchContext.storyboardRendererDecoderRecommendedLevelMethod by gettingFirstMethodDeclaratively("#-1#") { + returnType("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("L") - strings("#-1#") + parameterTypes("L") } -internal val subtitleTrackFingerprint = fingerprint { +internal val BytecodePatchContext.subtitleTrackMethod by gettingFirstMethodDeclaratively("DISABLE_CAPTIONS_OPTION") { + definingClass("/SubtitleTrack;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() + returnType("Z") + parameterTypes() opcodes( Opcode.CONST_STRING, Opcode.INVOKE_VIRTUAL, @@ -33,8 +33,4 @@ internal val subtitleTrackFingerprint = fingerprint { Opcode.MOVE_RESULT, Opcode.RETURN, ) - strings("DISABLE_CAPTIONS_OPTION") - custom { _, classDef -> - classDef.endsWith("/SubtitleTrack;") - } } 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..3859ce093d 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 @@ -1,11 +1,12 @@ package app.revanced.patches.youtube.layout.branding +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patches.shared.layout.branding.baseCustomBrandingPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_MAIN_ACTIVITY_NAME import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.misc.settings.PreferenceScreen -import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod @Suppress("unused") val customBrandingPatch = baseCustomBrandingPatch( @@ -15,9 +16,9 @@ val customBrandingPatch = baseCustomBrandingPatch( originalAppPackageName = YOUTUBE_PACKAGE_NAME, isYouTubeMusic = false, numberOfPresetAppNames = 5, - mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + getMainActivityOnCreate = BytecodePatchContext::mainActivityOnCreateMethod::get, mainActivityName = YOUTUBE_MAIN_ACTIVITY_NAME, - activityAliasNameWithIntents = "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity", + activityAliasNameWithIntents = $$"com.google.android.youtube.app.honeycomb.Shell$HomeActivity", preferenceScreen = PreferenceScreen.GENERAL_LAYOUT, block = { @@ -25,11 +26,11 @@ 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..261b0a0d80 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 @@ -1,7 +1,9 @@ package app.revanced.patches.youtube.layout.branding.header -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.wideLiteral +import app.revanced.patcher.firstMethod import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch @@ -10,16 +12,15 @@ 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.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 import app.revanced.util.Utils.trimIndentMultiline import app.revanced.util.copyResources import app.revanced.util.findElementByAttributeValueOrThrow -import app.revanced.util.forEachLiteralValueInstruction +import app.revanced.util.forEachInstructionAsSequence import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import java.io.File @@ -29,7 +30,7 @@ private val targetResourceDirectoryNames = mapOf( "drawable-hdpi" to "194x72 px", "drawable-xhdpi" to "258x96 px", "drawable-xxhdpi" to "387x144 px", - "drawable-xxxhdpi" to "512x192 px" + "drawable-xxxhdpi" to "512x192 px", ) /** @@ -57,34 +58,39 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/ private val changeHeaderBytecodePatch = bytecodePatch { dependsOn( resourceMappingPatch, - addBrandLicensePatch + addBrandLicensePatch, ) - execute { + apply { // Verify images exist. Resources are not used during patching but extension code does. arrayOf( "yt_ringo2_wordmark_header", - "yt_ringo2_premium_wordmark_header" + "yt_ringo2_premium_wordmark_header", ).forEach { resource -> variants.forEach { theme -> - resourceMappings["drawable", resource + "_" + theme] + ResourceType.DRAWABLE[resource + "_" + theme] } } arrayOf( "ytWordmarkHeader", - "ytPremiumWordmarkHeader" + "ytPremiumWordmarkHeader", ).forEach { resourceName -> - val resourceId = resourceMappings["attr", resourceName] + val id = ResourceType.ATTR[resourceName] - forEachLiteralValueInstruction(resourceId) { literalIndex -> - val register = getInstruction(literalIndex).registerA - addInstructions( - literalIndex + 1, + forEachInstructionAsSequence({ _, method, instruction, index -> + if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null + + val register = method.getInstruction(index).registerA + + return@forEachInstructionAsSequence index to register + }) { method, (index, register) -> + method.addInstructions( + index + 1, """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getHeaderAttributeId(I)I move-result v$register - """ + """, ) } } @@ -100,16 +106,15 @@ 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", + ), ) val custom by stringOption( - key = "custom", - title = "Custom header logo", + name = "Custom header logo", description = """ Folder with images to use as a custom header logo. @@ -121,10 +126,10 @@ val changeHeaderPatch = resourcePatch( The image dimensions must be as follows: ${targetResourceDirectoryNames.map { (dpi, dim) -> "- $dpi: $dim" }.joinToString("\n")} - """.trimIndentMultiline() + """.trimIndentMultiline(), ) - execute { + apply { addResources("youtube", "layout.branding.changeHeaderPatch") PreferenceScreen.GENERAL_LAYOUT.addPreferences( @@ -134,9 +139,9 @@ val changeHeaderPatch = resourcePatch( ListPreference( key = "revanced_header_logo", entriesKey = "revanced_header_logo_custom_entries", - entryValuesKey = "revanced_header_logo_custom_entry_values" + entryValuesKey = "revanced_header_logo_custom_entry_values", ) - } + }, ) logoResourceNames.forEach { logo -> @@ -145,8 +150,8 @@ val changeHeaderPatch = resourcePatch( "change-header", ResourceGroup( "drawable", - logo + "_" + variant + ".xml" - ) + logo + "_" + variant + ".xml", + ), ) } } @@ -159,8 +164,8 @@ val changeHeaderPatch = resourcePatch( "change-header", ResourceGroup( dpi, - *customHeaderResourceFileNames - ) + *customHeaderResourceFileNames, + ), ) } } @@ -189,10 +194,11 @@ val changeHeaderPatch = resourcePatch( "Base.Theme.YouTube.Light" to "light", "Base.Theme.YouTube.Dark" to "dark", "CairoLightThemeRingo2Updates" to "light", - "CairoDarkThemeRingo2Updates" to "dark" + "CairoDarkThemeRingo2Updates" to "dark", ).forEach { (style, mode) -> val styleElement = document.childNodes.findElementByAttributeValueOrThrow( - "name", style + "name", + style, ) fun addDrawableElement(document: Document, logoName: String, mode: String) { @@ -214,21 +220,24 @@ val changeHeaderPatch = resourcePatch( if (custom != null) { val customFile = File(custom!!.trim()) if (!customFile.exists()) { - throw PatchException("The custom header path cannot be found: " + - customFile.absolutePath + throw PatchException( + "The custom header path cannot be found: " + + customFile.absolutePath, ) } if (!customFile.isDirectory) { - throw PatchException("The custom header path must be a folder: " - + customFile.absolutePath) + throw PatchException( + "The custom header path must be a folder: " + + customFile.absolutePath, + ) } var copiedFiles = false // For each source folder, copy the files to the target resource directories. - customFile.listFiles { - file -> file.isDirectory && file.name in targetResourceDirectoryNames + customFile.listFiles { file -> + file.isDirectory && file.name in targetResourceDirectoryNames }!!.forEach { dpiSourceFolder -> val targetDpiFolder = get("res").resolve(dpiSourceFolder.name) if (!targetDpiFolder.exists()) { @@ -241,8 +250,10 @@ val changeHeaderPatch = resourcePatch( }!! if (customFiles.isNotEmpty() && customFiles.size != variants.size) { - throw PatchException("Both light/dark mode images " + - "must be specified but only found: " + customFiles.map { it.name }) + throw PatchException( + "Both light/dark mode images " + + "must be specified but only found: " + customFiles.map { it.name }, + ) } customFiles.forEach { imgSourceFile -> @@ -254,9 +265,11 @@ val changeHeaderPatch = resourcePatch( } if (!copiedFiles) { - throw PatchException("Expected to find directories and files: " - + customHeaderResourceFileNames.contentToString() - + "\nBut none were found in the provided option file path: " + customFile.absolutePath) + throw PatchException( + "Expected to find directories and files: " + + customHeaderResourceFileNames.contentToString() + + "\nBut none were found in the provided option file path: " + customFile.absolutePath, + ) } } } 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 f48a3b3c25..c614b2ebb9 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,9 +8,13 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.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( +@Suppress("unused") +val hideVideoActionButtonsPatch = resourcePatch( name = "Hide video action buttons", description = "Adds options to hide action buttons (such as the Download button) under videos.", ) { @@ -18,43 +22,62 @@ 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 { + apply { 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;") + addLithoFilter("Lapp/revanced/extension/youtube/patches/litho/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..35d51ba015 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,65 @@ package app.revanced.patches.youtube.layout.buttons.navigation -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import com.android.tools.smali.dexlib2.Opcode -internal const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive" - -internal val addCreateButtonViewFingerprint = fingerprint { - strings("Android Wear", ANDROID_AUTOMOTIVE_STRING) +internal val BytecodePatchContext.addCreateButtonViewMethodMatch by composingFirstMethod { + instructions( + "Android Wear"(), + Opcode.IF_EQZ(), + after("Android Automotive"()), + ) } -internal val createPivotBarFingerprint = fingerprint { +internal val BytecodePatchContext.createPivotBarMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - parameters( + parameterTypes( "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;", "Landroid/widget/TextView;", "Ljava/lang/CharSequence;", ) - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.RETURN_VOID, + instructions( + method { name == "setText" && definingClass == "Landroid/widget/TextView;" }, + Opcode.RETURN_VOID(), ) } -internal const val TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG = 45400535L - -internal val translucentNavigationStatusBarFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.animatedNavigationTabsFeatureFlagMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - literal { TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG } + returnType("Z") + instructions( + 45680008L(), + ) } -internal const val TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG = 45630927L - -internal val translucentNavigationButtonsFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.translucentNavigationStatusBarFeatureFlagMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - literal { TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG } + returnType("Z") + instructions( + 45400535L(), // Translucent status bar feature flag. + ) } /** - * The device on screen back/home/recent buttons. + * YouTube nav buttons. */ -internal const val TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG = 45632194L - -internal val translucentNavigationButtonsSystemFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.translucentNavigationButtonsFeatureFlagMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - literal { TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG } -} \ No newline at end of file + returnType("V") + instructions( + 45630927L(), // Translucent navigation bar buttons feature flag. + ) +} + +/** + * Device on screen back/home/recent buttons. + */ +internal val BytecodePatchContext.translucentNavigationButtonsSystemFeatureFlagMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Z") + instructions( + 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..230db65c62 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 @@ -1,8 +1,8 @@ package app.revanced.patches.youtube.layout.buttons.navigation -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.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -13,19 +13,18 @@ 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;" +@Suppress("ObjectPropertyName") val navigationButtonsPatch = bytecodePatch( name = "Navigation buttons", description = "Adds options to hide and change navigation buttons (such as the Shorts button).", @@ -35,19 +34,19 @@ val navigationButtonsPatch = bytecodePatch( settingsPatch, addResourcesPatch, navigationBarHookPatch, - versionCheckPatch + 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", + ), ) - execute { + apply { addResources("youtube", "layout.buttons.navigation.navigationButtonsPatch") val preferences = mutableSetOf( @@ -65,7 +64,13 @@ val navigationButtonsPatch = bytecodePatch( preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark") PreferenceScreen.GENERAL_LAYOUT.addPreferences( - SwitchPreference("revanced_disable_translucent_status_bar") + SwitchPreference("revanced_disable_translucent_status_bar"), + ) + } + + if (is_20_15_or_greater) { + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + SwitchPreference("revanced_navigation_bar_animations"), ) } @@ -73,17 +78,13 @@ val navigationButtonsPatch = bytecodePatch( PreferenceScreenPreference( key = "revanced_navigation_buttons_screen", sorting = Sorting.UNSORTED, - preferences = preferences - ) + preferences = preferences, + ), ) // Switch create with notifications button. - addCreateButtonViewFingerprint.method.apply { - val stringIndex = addCreateButtonViewFingerprint.stringMatches!!.find { match -> - match.string == ANDROID_AUTOMOTIVE_STRING - }!!.index - - val conditionalCheckIndex = stringIndex - 1 + addCreateButtonViewMethodMatch.method.apply { + val conditionalCheckIndex = addCreateButtonViewMethodMatch[1] val conditionRegister = getInstruction(conditionalCheckIndex).registerA @@ -97,40 +98,53 @@ val navigationButtonsPatch = bytecodePatch( } // Hide navigation button labels. - createPivotBarFingerprint.method.apply { - val setTextIndex = indexOfFirstInstructionOrThrow { - getReference()?.name == "setText" + createPivotBarMethodMatch.let { + it.method.apply { + val setTextIndex = it[0] + 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", - ) + translucentNavigationStatusBarFeatureFlagMethodMatch.let { + it.method.insertLiteralOverride( + it[0], + "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z", + ) + } - translucentNavigationButtonsFeatureFlagFingerprint.method.insertLiteralOverride( - TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", - ) + translucentNavigationButtonsFeatureFlagMethodMatch.let { + it.method.insertLiteralOverride( + it[0], + "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", + ) + } - translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertLiteralOverride( - TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", - ) + translucentNavigationButtonsSystemFeatureFlagMethodMatch.let { + it.method.insertLiteralOverride( + it[0], + "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", + ) + } + } + + if (is_20_15_or_greater) { + animatedNavigationTabsFeatureFlagMethodMatch.let { + it.method.insertLiteralOverride( + it[0], + "$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..da59a908f4 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,30 +1,32 @@ 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.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType 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 BytecodePatchContext.mediaRouteButtonMethod by gettingFirstMethodDeclaratively { + name("setVisibility") + definingClass("/MediaRouteButton;") + parameterTypes("I") } -internal val mediaRouteButtonFingerprint = fingerprint { - parameters("I") - custom { methodDef, _ -> - methodDef.definingClass.endsWith("/MediaRouteButton;") && methodDef.name == "setVisibility" - } +internal val BytecodePatchContext.castButtonPlayerFeatureFlagMethodMatch by composingFirstMethod { + returnType("Z") + instructions(45690091L()) } -internal val inflateControlsGroupLayoutStubFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters() - returns("V") - literal { controlsButtonGroupLayoutStub } +internal val BytecodePatchContext.castButtonActionFeatureFlagMethodMatch by composingFirstMethod { + returnType("Z") + instructions(45690090L()) +} + +internal val BytecodePatchContext.inflateControlsGroupLayoutStubMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameterTypes() + returnType("V") + instructions( + ResourceType.ID("youtube_controls_button_group_layout_stub"), + method("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..4039e8a7cb 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 @@ -1,49 +1,28 @@ package app.revanced.patches.youtube.layout.buttons.overlay -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.* 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.patches.youtube.shared.getLayoutConstructorMethodMatch +import app.revanced.patches.youtube.shared.subtitleButtonControllerMethod import app.revanced.util.* 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;" +@Suppress("ObjectPropertyName") val hidePlayerOverlayButtonsPatch = bytecodePatch( name = "Hide player overlay buttons", description = "Adds options to hide the player Cast, Autoplay, Captions, Previous & Next buttons, and the player " + @@ -53,19 +32,20 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( sharedExtensionPatch, settingsPatch, addResourcesPatch, - hidePlayerOverlayButtonsResourcePatch, + resourceMappingPatch, // Used for finding methods. + 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", + ), ) - execute { + apply { addResources("youtube", "layout.buttons.overlay.hidePlayerOverlayButtonsPatch") PreferenceScreen.PLAYER.addPreferences( @@ -78,17 +58,11 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( // region Hide player next/previous button. - playerControlsPreviousNextOverlayTouchFingerprint.method.apply { - val resourceIndex = indexOfFirstLiteralInstructionOrThrow(playerControlPreviousButtonTouchArea) + getLayoutConstructorMethodMatch().let { + val insertIndex = it[-1] + val viewRegister = it.method.getInstruction(insertIndex).registerC - val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) { - opcode == Opcode.INVOKE_STATIC && - getReference()?.parameterTypes?.firstOrNull() == "Landroid/view/View;" - } - - val viewRegister = getInstruction(insertIndex).registerC - - addInstruction( + it.method.addInstruction( insertIndex, "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR" + "->hidePreviousNextButtons(Landroid/view/View;)V", @@ -99,7 +73,7 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( // region Hide cast button. - mediaRouteButtonFingerprint.method.addInstructions( + mediaRouteButtonMethod.addInstructions( 0, """ invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getCastButtonOverrideV2(I)I @@ -107,17 +81,28 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( """, ) + if (is_20_28_or_greater) { + arrayOf( + castButtonPlayerFeatureFlagMethodMatch, + castButtonActionFeatureFlagMethodMatch, + ).forEach { match -> + match.method.insertLiteralOverride( + match[0], + "$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 + subtitleButtonControllerMethod.apply { 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", ) } @@ -125,7 +110,7 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( // region Hide autoplay button. - layoutConstructorFingerprint.method.apply { + getLayoutConstructorMethodMatch().method.apply { val constIndex = indexOfFirstResourceIdOrThrow("autonav_toggle") val constRegister = getInstruction(constIndex).registerA @@ -152,27 +137,21 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch( // region Hide player control buttons background. - inflateControlsGroupLayoutStubFingerprint.method.apply { - val controlsButtonGroupLayoutStubResIdConstIndex = - indexOfFirstLiteralInstructionOrThrow(controlsButtonGroupLayoutStub) - val inflateControlsGroupLayoutStubIndex = - indexOfFirstInstruction(controlsButtonGroupLayoutStubResIdConstIndex) { - getReference()?.name == "inflate" - } + inflateControlsGroupLayoutStubMethodMatch.let { + it.method.apply { + val insertIndex = it[-1] + 1 + val freeRegister = findFreeRegister(insertIndex) - 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 - """ - ) + 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..e4a5d1d1a8 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,8 @@ package app.revanced.patches.youtube.layout.formfactor -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.* +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -11,11 +12,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;" @@ -28,45 +26,52 @@ val changeFormFactorPatch = bytecodePatch( sharedExtensionPatch, settingsPatch, addResourcesPatch, - navigationButtonsPatch + navigationButtonsPatch, ) 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 { + apply { addResources("youtube", "layout.formfactor.changeFormFactorPatch") PreferenceScreen.GENERAL_LAYOUT.addPreferences( - ListPreference("revanced_change_form_factor") + ListPreference("revanced_change_form_factor"), ) hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR) - createPlayerRequestBodyWithModelFingerprint.method.apply { - val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type + val formFactorEnumConstructorClass = formFactorEnumConstructorMethod.definingClass - val index = indexOfFirstInstructionOrThrow { - val reference = getReference() - opcode == Opcode.IGET && - reference?.definingClass == formFactorEnumClass && - reference.type == "I" - } - val register = getInstruction(index).registerA - - addInstructions( - index + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getFormFactor(I)I - move-result v$register - """ + val createPlayerRequestBodyWithModelMatch = firstMethodComposite { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("L") + parameterTypes() + instructions( + field { name == "MODEL" && definingClass == "Landroid/os/Build;" }, + field { type == "I" && definingClass == formFactorEnumConstructorClass }, ) } + + createPlayerRequestBodyWithModelMatch.let { + it.method.apply { + val index = it[-1] + val register = getInstruction(index).registerA + + 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..8aa97f38d7 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,49 +1,15 @@ package app.revanced.patches.youtube.layout.formfactor -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext 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 { +internal val BytecodePatchContext.formFactorEnumConstructorMethod by gettingFirstImmutableMethodDeclaratively( + "UNKNOWN_FORM_FACTOR", + "SMALL_FORM_FACTOR", + "LARGE_FORM_FACTOR", + "AUTOMOTIVE_FORM_FACTOR", +) { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - strings( - "UNKNOWN_FORM_FACTOR", - "SMALL_FORM_FACTOR", - "LARGE_FORM_FACTOR", - "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..b09e058942 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 @@ -1,6 +1,7 @@ package app.revanced.patches.youtube.layout.hide.endscreencards -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.containsLiteralInstruction import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction @@ -9,8 +10,10 @@ import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.reference.FieldReference -internal val layoutCircleFingerprint = fingerprint { - returns("Landroid/view/View;") +internal val BytecodePatchContext.layoutCircleMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameterTypes() + returnType("Landroid/view/View;") opcodes( Opcode.CONST, Opcode.CONST_4, @@ -21,8 +24,10 @@ internal val layoutCircleFingerprint = fingerprint { literal { layoutCircle } } -internal val layoutIconFingerprint = fingerprint { - returns("Landroid/view/View;") +internal val BytecodePatchContext.layoutIconMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameterTypes() + returnType("Landroid/view/View;") opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT, @@ -32,8 +37,10 @@ internal val layoutIconFingerprint = fingerprint { literal { layoutIcon } } -internal val layoutVideoFingerprint = fingerprint { - returns("Landroid/view/View;") +internal val BytecodePatchContext.layoutVideoMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC) + parameterTypes() + returnType("Landroid/view/View;") opcodes( Opcode.CONST, Opcode.CONST_4, @@ -44,18 +51,18 @@ internal val layoutVideoFingerprint = fingerprint { literal { layoutVideo } } -internal val showEndscreenCardsFingerprint = fingerprint { +internal val BytecodePatchContext.showEndscreenCardsMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - custom { method, classDef -> - classDef.methods.count() == 5 - && method.containsLiteralInstruction(0) - && method.containsLiteralInstruction(5) - && method.containsLiteralInstruction(8) - && method.indexOfFirstInstruction { - val reference = getReference() - reference?.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" - } >= 0 + returnType("V") + parameterTypes("L") + custom { + immutableClassDef.methods.count() == 5 && + containsLiteralInstruction(0) && + containsLiteralInstruction(5) && + containsLiteralInstruction(8) && + indexOfFirstInstruction { + val reference = getReference() + reference?.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" + } >= 0 } -} \ No newline at end of file +} 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..ca2fca5c35 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 @@ -1,15 +1,14 @@ package app.revanced.patches.youtube.layout.hide.endscreencards -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.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.ResourceType 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 @@ -32,14 +31,14 @@ private val hideEndScreenCardsResourcePatch = resourcePatch { addResourcesPatch, ) - execute { + apply { addResources("youtube", "layout.hide.endscreencards.hideEndScreenCardsResourcePatch") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_hide_endscreen_cards"), ) - fun idOf(name: String) = resourceMappings["layout", "endscreen_element_layout_$name"] + fun idOf(name: String) = ResourceType.LAYOUT["endscreen_element_layout_$name"] layoutCircle = idOf("circle") layoutIcon = idOf("icon") @@ -58,38 +57,38 @@ val hideEndScreenCardsPatch = bytecodePatch( dependsOn( sharedExtensionPatch, hideEndScreenCardsResourcePatch, - versionCheckPatch + 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", + ), ) - execute { + apply { listOf( - layoutCircleFingerprint, - layoutIconFingerprint, - layoutVideoFingerprint, - ).forEach { fingerprint -> - fingerprint.method.apply { - val insertIndex = fingerprint.patternMatch!!.endIndex + 1 + layoutCircleMethodMatch, + layoutIconMethodMatch, + layoutVideoMethodMatch, + ).forEach { match -> + match.method.apply { + val insertIndex = match[-1] + 1 val viewRegister = getInstruction(insertIndex - 1).registerA addInstruction( insertIndex, "invoke-static { v$viewRegister }, " + - "$EXTENSION_CLASS_DESCRIPTOR->hideEndScreenCardView(Landroid/view/View;)V", + "$EXTENSION_CLASS_DESCRIPTOR->hideEndScreenCardView(Landroid/view/View;)V", ) } } if (is_19_43_or_greater) { - showEndscreenCardsFingerprint.method.addInstructionsWithLabels( + showEndscreenCardsMethod.addInstructionsWithLabels( 0, """ invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideEndScreenCards()Z @@ -98,7 +97,7 @@ val hideEndScreenCardsPatch = bytecodePatch( return-void :show nop - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/Fingerprints.kt index 887963e56e..ebb221781d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreensuggestion/Fingerprints.kt @@ -1,38 +1,38 @@ package app.revanced.patches.youtube.layout.hide.endscreensuggestion -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction +import app.revanced.patcher.* +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val autoNavConstructorFingerprint = fingerprint { - returns("V") +internal val BytecodePatchContext.autoNavConstructorMethod by gettingFirstImmutableMethodDeclaratively("main_app_autonav") { + returnType("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - strings("main_app_autonav") } -internal val autoNavStatusFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getAutoNavStatusMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() + returnType("Z") + parameterTypes() } -internal val removeOnLayoutChangeListenerFingerprint = fingerprint { +internal val BytecodePatchContext.removeOnLayoutChangeListenerMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() + returnType("V") + parameterTypes() opcodes( Opcode.IPUT, - Opcode.INVOKE_VIRTUAL + Opcode.INVOKE_VIRTUAL, ) // This is the only reference present in the entire smali. - custom { method, _ -> - method.indexOfFirstInstruction { - val reference = getReference() - reference?.name == "removeOnLayoutChangeListener" && - reference.definingClass.endsWith("/YouTubePlayerOverlaysLayout;") - } >= 0 + custom { + instructions.anyInstruction { + val reference = methodReference + reference?.name == "removeOnLayoutChangeListener" && reference.definingClass.endsWith("/YouTubePlayerOverlaysLayout;") + } } -} \ No newline at end of file +} 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..979621b5e1 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 @@ -1,20 +1,20 @@ package app.revanced.patches.youtube.layout.hide.endscreensuggestion -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen -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.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideEndScreenSuggestedVideoPatch;" @@ -31,34 +31,33 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_end_screen_suggested_video"), ) - removeOnLayoutChangeListenerFingerprint.let { - val endScreenMethod = navigate(it.originalMethod).to(it.patternMatch!!.endIndex).stop() + removeOnLayoutChangeListenerMethodMatch.let { + val endScreenMethod = navigate(it.immutableMethod).to(it[-1]).stop() endScreenMethod.apply { - val autoNavStatusMethodName = autoNavStatusFingerprint.match( - autoNavConstructorFingerprint.classDef - ).originalMethod.name + val autoNavStatusMethodName = autoNavConstructorMethod.immutableClassDef.getAutoNavStatusMethod().name val invokeIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() + val reference = methodReference reference?.name == autoNavStatusMethodName && - reference.returnType == "Z" && - reference.parameterTypes.isEmpty() + reference.returnType == "Z" && + reference.parameterTypes.isEmpty() } + val iGetObjectIndex = indexOfFirstInstructionReversedOrThrow(invokeIndex, Opcode.IGET_OBJECT) val invokeReference = getInstruction(invokeIndex).reference val iGetObjectReference = getInstruction(iGetObjectIndex).reference @@ -81,7 +80,7 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch( if-nez v0, :show_end_screen_recommendation return-void """, - ExternalLabel("show_end_screen_recommendation", getInstruction(0)) + ExternalLabel("show_end_screen_recommendation", getInstruction(0)), ) } } 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..8d048b6458 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 @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.layout.hide.fullscreenambientmode -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -17,6 +17,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;" +@Suppress("unused") val disableFullscreenAmbientModePatch = bytecodePatch( name = "Disable fullscreen ambient mode", description = "Adds an option to disable the ambient mode when in fullscreen.", @@ -29,21 +30,21 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.hide.fullscreenambientmode.disableFullscreenAmbientModePatch") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_disable_fullscreen_ambient_mode"), ) - setFullScreenBackgroundColorFingerprint.method.apply { + setFullScreenBackgroundColorMethod.apply { val insertIndex = indexOfFirstInstructionReversedOrThrow { getReference()?.name == "setBackgroundColor" } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt index b619020878..57e335d8b2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt @@ -1,14 +1,13 @@ package app.revanced.patches.youtube.layout.hide.fullscreenambientmode -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags -internal val setFullScreenBackgroundColorFingerprint = fingerprint { - returns("V") +internal val BytecodePatchContext.setFullScreenBackgroundColorMethod by gettingFirstMethodDeclaratively { + name("onLayout") + definingClass("/YouTubePlayerViewNotForReflection;") + returnType("V") accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) - parameters("Z", "I", "I", "I", "I") - custom { method, classDef -> - classDef.type.endsWith("/YouTubePlayerViewNotForReflection;") - && method.name == "onLayout" - } + parameterTypes("Z", "I", "I", "I", "I") } 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..73180a043c 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,84 +1,74 @@ package app.revanced.patches.youtube.layout.hide.general -import app.revanced.patcher.fingerprint -import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutFingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutMethod 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 } -} +import com.android.tools.smali.dexlib2.iface.ClassDef /** - * 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 BytecodePatchContext.hideShowMoreButtonMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL, AccessFlags.SYNTHETIC) + returnType("V") + parameterTypes("L", "Ljava/lang/Object;") + instructions( + ResourceType.LAYOUT("expand_button_down"), + method { toString() == "Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View;" }, + after(Opcode.MOVE_RESULT_OBJECT()), ) - 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 BytecodePatchContext.hideShowMoreLegacyButtonMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + instructions( + ResourceType.LAYOUT("expand_button_down"), + method { toString() == "Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;" }, + Opcode.MOVE_RESULT_OBJECT(), ) - strings("Failed to parse Element") // String is a partial match. } -/** - * 19.01 - 20.06 - */ -internal val parseElementFromBufferLegacy1901Fingerprint = fingerprint { - parameters("L", "L", "[B", "L", "L") - opcodes( - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, +internal val BytecodePatchContext.parseElementFromBufferMethodMatch by composingFirstMethod { + parameterTypes("L", "L", "[B", "L", "L") + instructions( + Opcode.IGET_OBJECT(), + // IGET_BOOLEAN // 20.07+ + afterAtMost(1, Opcode.INVOKE_INTERFACE()), + after(Opcode.MOVE_RESULT_OBJECT()), + "Failed to parse Element"(String::startsWith), ) - strings("Failed to parse Element") // String is a partial match. } -internal val playerOverlayFingerprint = fingerprint { +internal val BytecodePatchContext.playerOverlayMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - strings("player_overlay_in_video_programming") + returnType("L") + instructions( + "player_overlay_in_video_programming"(), + ) } -internal val showWatermarkFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getShowWatermarkMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L", "L") + returnType("V") + parameterTypes("L", "L") } /** - * Matches same method as [wideSearchbarLayoutFingerprint]. + * Matches same method as [wideSearchbarLayoutMethod]. */ -internal val yoodlesImageViewFingerprint = fingerprint { +internal val BytecodePatchContext.yoodlesImageViewMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/view/View;") - parameters("L", "L") - literal { youTubeLogo } + returnType("Landroid/view/View;") + parameterTypes("L", "L") + instructions(ResourceType.ID("youtube_logo")) } -internal val crowdfundingBoxFingerprint = fingerprint { +internal val BytecodePatchContext.crowdfundingBoxMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) opcodes( Opcode.INVOKE_VIRTUAL, @@ -88,7 +78,7 @@ internal val crowdfundingBoxFingerprint = fingerprint { literal { crowdfundingBoxId } } -internal val albumCardsFingerprint = fingerprint { +internal val BytecodePatchContext.albumCardsMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) opcodes( Opcode.MOVE_RESULT_OBJECT, @@ -101,9 +91,8 @@ internal val albumCardsFingerprint = fingerprint { literal { albumCardId } } -internal val filterBarHeightFingerprint = fingerprint { +internal val BytecodePatchContext.filterBarHeightMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CONST, Opcode.INVOKE_VIRTUAL, @@ -113,9 +102,8 @@ internal val filterBarHeightFingerprint = fingerprint { literal { filterBarHeightId } } -internal val relatedChipCloudFingerprint = fingerprint { +internal val BytecodePatchContext.relatedChipCloudMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CONST, Opcode.INVOKE_VIRTUAL, @@ -124,9 +112,8 @@ internal val relatedChipCloudFingerprint = fingerprint { literal { relatedChipCloudMarginId } } -internal val searchResultsChipBarFingerprint = fingerprint { +internal val BytecodePatchContext.searchResultsChipBarMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CONST, Opcode.INVOKE_VIRTUAL, @@ -137,27 +124,25 @@ internal val searchResultsChipBarFingerprint = fingerprint { literal { barContainerHeightId } } -internal val showFloatingMicrophoneButtonFingerprint = fingerprint { +internal val BytecodePatchContext.showFloatingMicrophoneButtonMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - opcodes( - Opcode.IGET_BOOLEAN, - Opcode.IF_EQZ, + returnType("V") + parameterTypes() + instructions( + ResourceType.ID("fab"), + afterAtMost(10, allOf(Opcode.CHECK_CAST(), type { endsWith("/FloatingActionButton;") })), + afterAtMost(10, Opcode.IGET_BOOLEAN()), ) - literal { fabButtonId } } -internal val hideViewCountFingerprint = fingerprint { +internal val BytecodePatchContext.hideViewCountMethodMatch by composingFirstMethod( + "Has attachmentRuns but drawableRequester is missing.", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Ljava/lang/CharSequence;") - + returnType("Ljava/lang/CharSequence;") opcodes( Opcode.RETURN_OBJECT, Opcode.CONST_STRING, Opcode.RETURN_OBJECT, ) - strings( - "Has attachmentRuns but drawableRequester is missing.", - ) -} \ No newline at end of file +} 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 49e38337b1..1abf4833c2 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 @@ -1,29 +1,24 @@ package app.revanced.patches.youtube.layout.hide.general -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.Match -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.CompositeMatch +import app.revanced.patcher.extensions.* +import app.revanced.patcher.immutableClassDef 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.shared.layout.hide.general.hideLayoutComponentsPatch -import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.ResourceType 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.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.* +import app.revanced.util.findFreeRegister +import app.revanced.util.findInstructionIndicesReversedOrThrow +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionReversedOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction @@ -31,80 +26,43 @@ 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.MethodReference -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", - ] + apply { + albumCardId = ResourceType.LAYOUT["album_card"] - albumCardId = resourceMappings[ - "layout", - "album_card", - ] + crowdfundingBoxId = ResourceType.LAYOUT["donation_companion"] - crowdfundingBoxId = resourceMappings[ - "layout", - "donation_companion", - ] + relatedChipCloudMarginId = ResourceType.LAYOUT["related_chip_cloud_reduced_margins"] - youTubeLogo = resourceMappings[ - "id", - "youtube_logo", - ] + filterBarHeightId = ResourceType.DIMEN["filter_bar_height"] - relatedChipCloudMarginId = resourceMappings[ - "layout", - "related_chip_cloud_reduced_margins", - ] - - filterBarHeightId = resourceMappings[ - "dimen", - "filter_bar_height", - ] - - barContainerHeightId = resourceMappings[ - "dimen", - "bar_container_height", - ] - - fabButtonId = resourceMappings[ - "id", - "fab", - ] + barContainerHeightId = ResourceType.DIMEN["bar_container_height"] } } private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR = - "Lapp/revanced/extension/youtube/patches/components/LayoutComponentsFilter;" + "Lapp/revanced/extension/youtube/patches/litho/LayoutComponentsFilter;" private const val DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME = - "Lapp/revanced/extension/youtube/patches/components/DescriptionComponentsFilter;" + "Lapp/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter;" private const val COMMENTS_FILTER_CLASS_NAME = - "Lapp/revanced/extension/youtube/patches/components/CommentsFilter;" + "Lapp/revanced/extension/youtube/patches/litho/CommentsFilter;" private const val CUSTOM_FILTER_CLASS_NAME = - "Lapp/revanced/extension/shared/patches/components/CustomFilter;" + "Lapp/revanced/extension/shared/patches/litho/CustomFilter;" private const val KEYWORD_FILTER_CLASS_NAME = - "Lapp/revanced/extension/youtube/patches/components/KeywordContentFilter;" - + "Lapp/revanced/extension/youtube/patches/litho/KeywordContentFilter;" val hideLayoutComponentsPatch = hideLayoutComponentsPatch( lithoFilterPatch = lithoFilterPatch, @@ -112,22 +70,24 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( additionalDependencies = setOf( hideLayoutComponentsResourcePatch, navigationBarHookPatch, + versionCheckPatch, + resourceMappingPatch, ), filterClasses = setOf( LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR, DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME, COMMENTS_FILTER_CLASS_NAME, KEYWORD_FILTER_CLASS_NAME, - CUSTOM_FILTER_CLASS_NAME + CUSTOM_FILTER_CLASS_NAME, ), compatibleWithPackages = arrayOf( "com.google.android.youtube" to setOf( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", - ) - ) + "20.21.37", + "20.31.40", + ), + ), ) { addResources("youtube", "layout.hide.general.hideLayoutComponentsPatch") @@ -190,7 +150,7 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE), NonInteractivePreference( key = "revanced_hide_keyword_content_about", - tag = "app.revanced.extension.shared.settings.preference.BulletPointPreference" + tag = "app.revanced.extension.shared.settings.preference.BulletPointPreference", ), NonInteractivePreference( key = "revanced_hide_keyword_content_about_whole_words", @@ -228,7 +188,7 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( SwitchPreference("revanced_hide_floating_microphone_button"), SwitchPreference( key = "revanced_hide_horizontal_shelves", - tag = "app.revanced.extension.shared.settings.preference.BulletPointSwitchPreference" + tag = "app.revanced.extension.shared.settings.preference.BulletPointSwitchPreference", ), SwitchPreference("revanced_hide_image_shelf"), SwitchPreference("revanced_hide_latest_posts"), @@ -248,31 +208,30 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( // region Mix playlists - (if (is_20_09_or_greater) parseElementFromBufferFingerprint - else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint - else parseElementFromBufferLegacy1901Fingerprint).let { + parseElementFromBufferMethodMatch.let { it.method.apply { + val startIndex = it[0] + val insertIndex = startIndex + 1 + 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 - """, + 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), ) } @@ -282,9 +241,7 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( // region Watermark (legacy code for old versions of YouTube) - showWatermarkFingerprint.match( - playerOverlayFingerprint.originalClassDef, - ).method.apply { + playerOverlayMethod.immutableClassDef.getShowWatermarkMethod().apply { val index = implementation!!.instructions.size - 5 removeInstruction(index) @@ -301,30 +258,32 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( // region Show more button - hideShowMoreButtonFingerprint.method.apply { - val moveRegisterIndex = hideShowMoreButtonFingerprint.patternMatch!!.endIndex - val viewRegister = getInstruction(moveRegisterIndex).registerA + (if (is_20_26_or_greater) hideShowMoreButtonMethodMatch else hideShowMoreLegacyButtonMethodMatch).let { + it.method.apply { + val moveRegisterIndex = it[-1] + val viewRegister = getInstruction(moveRegisterIndex).registerA - val insertIndex = moveRegisterIndex + 1 - addInstruction( - insertIndex, - "invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + + val insertIndex = moveRegisterIndex + 1 + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + "->hideShowMoreButton(Landroid/view/View;)V", - ) + ) + } } // endregion // region crowdfunding box - crowdfundingBoxFingerprint.let { + crowdfundingBoxMethodMatch.let { it.method.apply { - val insertIndex = it.patternMatch!!.endIndex + val insertIndex = it[-1] val objectRegister = getInstruction(insertIndex).registerA addInstruction( insertIndex, "invoke-static {v$objectRegister}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + - "->hideCrowdfundingBox(Landroid/view/View;)V", + "->hideCrowdfundingBox(Landroid/view/View;)V", ) } } @@ -333,16 +292,16 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( // region hide album cards - albumCardsFingerprint.let { + albumCardsMethodMatch.let { it.method.apply { - val checkCastAnchorIndex = it.patternMatch!!.endIndex + val checkCastAnchorIndex = it[-1] val insertIndex = checkCastAnchorIndex + 1 val register = getInstruction(checkCastAnchorIndex).registerA addInstruction( insertIndex, "invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + - "->hideAlbumCard(Landroid/view/View;)V", + "->hideAlbumCard(Landroid/view/View;)V", ) } } @@ -351,25 +310,26 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( // region hide floating microphone - showFloatingMicrophoneButtonFingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(fabButtonId) - val booleanIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.IGET_BOOLEAN) - val register = getInstruction(booleanIndex).registerA + showFloatingMicrophoneButtonMethodMatch.let { + it.method.apply { + val index = it[-1] + 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 // region 'Yoodles' - yoodlesImageViewFingerprint.method.apply { + yoodlesImageViewMethod.apply { findInstructionIndicesReversedOrThrow { getReference()?.name == "setImageDrawable" }.forEach { insertIndex -> @@ -379,41 +339,40 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( replaceInstruction( insertIndex, "invoke-static { v$imageViewRegister, v$drawableRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->" + - "setDoodleDrawable(Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;)V" + "setDoodleDrawable(Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;)V", ) } } // endregion - // region hide view count - hideViewCountFingerprint.method.apply { - val startIndex = hideViewCountFingerprint.patternMatch!!.startIndex + hideViewCountMethodMatch.method.apply { + val startIndex = hideViewCountMethodMatch[0] var returnStringRegister = getInstruction(startIndex).registerA // Find the instruction where the text dimension is retrieved. val applyDimensionIndex = indexOfFirstInstructionReversedOrThrow { val reference = getReference() opcode == Opcode.INVOKE_STATIC && - reference?.definingClass == "Landroid/util/TypedValue;" && - reference.returnType == "F" && - reference.name == "applyDimension" && - reference.parameterTypes == listOf("I", "F", "Landroid/util/DisplayMetrics;") + reference?.definingClass == "Landroid/util/TypedValue;" && + reference.returnType == "F" && + reference.name == "applyDimension" && + reference.parameterTypes == listOf("I", "F", "Landroid/util/DisplayMetrics;") } // A float value is passed which is used to determine subtitle text size. val floatDimensionRegister = getInstruction( - applyDimensionIndex + 1 + applyDimensionIndex + 1, ).registerA addInstructions( applyDimensionIndex - 1, """ - invoke-static { v$returnStringRegister, v$floatDimensionRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->modifyFeedSubtitleSpan(Landroid/text/SpannableString;F)Landroid/text/SpannableString; - move-result-object v$returnStringRegister - """ + invoke-static { v$returnStringRegister, v$floatDimensionRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->modifyFeedSubtitleSpan(Landroid/text/SpannableString;F)Landroid/text/SpannableString; + move-result-object v$returnStringRegister + """, ) } @@ -425,40 +384,38 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch( * Patch a [Method] with a given [instructions]. * * @param RegisterInstruction The type of instruction to get the register from. - * @param insertIndexOffset The offset to add to the end index of the [Match.patternMatch]. + * @param insertIndexOffset The offset to add to the end index of the [CompositeMatch.indices]. * @param hookRegisterOffset The offset to add to the register of the hook. * @param instructions The instructions to add with the register as a parameter. */ - fun Fingerprint.patch( + fun CompositeMatch.patch( insertIndexOffset: Int = 0, hookRegisterOffset: Int = 0, instructions: (Int) -> String, ) = method.apply { - val endIndex = patternMatch!!.endIndex - + val endIndex = get(-1) val insertIndex = endIndex + insertIndexOffset - val register = - getInstruction(endIndex + hookRegisterOffset).registerA + val register = getInstruction(endIndex + hookRegisterOffset).registerA addInstructions(insertIndex, instructions(register)) } - filterBarHeightFingerprint.patch { register -> + filterBarHeightMethodMatch.patch { register -> + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I + move-result v$register """ - invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I - move-result v$register - """ } - searchResultsChipBarFingerprint.patch(-1, -2) { register -> + searchResultsChipBarMethodMatch.patch(-1, -2) { register -> + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I + move-result v$register """ - invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I - move-result v$register - """ } - relatedChipCloudFingerprint.patch(1) { register -> + relatedChipCloudMethodMatch.patch(1) { register -> "invoke-static { v$register }, " + - "$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V" + "$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V" } } 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..322ea7a5a6 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,29 +1,34 @@ package app.revanced.patches.youtube.layout.hide.infocards -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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.ClassDef -internal val infocardsIncognitoFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getInfocardsIncognitoMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/Boolean;") - parameters("L", "J") - strings("vibrator") + returnType("Ljava/lang/Boolean;") + parameterTypes("L", "J") + instructions("vibrator"()) } -internal val infocardsIncognitoParentFingerprint = fingerprint { +internal val BytecodePatchContext.infocardsIncognitoParentMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - strings("player_overlay_info_card_teaser") -} - -internal val infocardsMethodCallFingerprint = fingerprint { - opcodes( - Opcode.INVOKE_VIRTUAL, - Opcode.IGET_OBJECT, - Opcode.INVOKE_INTERFACE, + returnType("Ljava/lang/String;") + instructions( + "player_overlay_info_card_teaser"(), ) - strings("Missing ControlsOverlayPresenter for InfoCards to work.") - literal { drawerResourceId } } + +internal val BytecodePatchContext.infocardsMethodCallMethodMatch by + composingFirstMethod("Missing ControlsOverlayPresenter for InfoCards to work.") { + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + ) + literal { drawerResourceId } + } 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 6a9779a497..d42b037990 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 @@ -1,19 +1,19 @@ package app.revanced.patches.youtube.layout.hide.infocards -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef 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.ResourceType 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.shared.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch @@ -25,22 +25,10 @@ internal var drawerResourceId = -1L private set private val hideInfocardsResourcePatch = resourcePatch { - dependsOn( - settingsPatch, - resourceMappingPatch, - addResourcesPatch, - ) - execute { - addResources("youtube", "layout.hide.infocards.hideInfocardsResourcePatch") + dependsOn(resourceMappingPatch) - PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_info_cards"), - ) - - drawerResourceId = resourceMappings[ - "id", - "info_cards_drawer_header", - ] + apply { + drawerResourceId = ResourceType.ID["info_cards_drawer_header"] } } @@ -53,19 +41,28 @@ 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 { - infocardsIncognitoFingerprint.match(infocardsIncognitoParentFingerprint.originalClassDef).method.apply { + apply { + 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. + infocardsIncognitoParentMethod.immutableClassDef.getInfocardsIncognitoMethod().apply { val invokeInstructionIndex = implementation!!.instructions.indexOfFirst { it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal && ((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V") @@ -78,26 +75,29 @@ val hideInfoCardsPatch = bytecodePatch( ) } - val hideInfoCardsCallMethod = infocardsMethodCallFingerprint.method + // Edit: This old non litho code may be obsolete and no longer used by any supported versions. + infocardsMethodCallMethodMatch.let { + val invokeInterfaceIndex = it[-1] + it.method.apply { + val register = implementation!!.registerCount - 1 - val invokeInterfaceIndex = infocardsMethodCallFingerprint.patternMatch!!.endIndex - val toggleRegister = infocardsMethodCallFingerprint.method.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;" + val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/litho/HideInfoCardsFilter;" addLithoFilter(filterClassDescriptor) } } 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 5f48570533..9fae036084 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 @@ -11,7 +11,8 @@ import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -val hidePlayerFlyoutMenuPatch = bytecodePatch( +@Suppress("unused") +val hidePlayerFlyoutMenuItemsPatch = bytecodePatch( name = "Hide player flyout menu items", description = "Adds options to hide menu items that appear when pressing the gear icon in the video player.", ) { @@ -24,15 +25,15 @@ 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", + ), ) - execute { - val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/components/PlayerFlyoutMenuItemsFilter;" + apply { + val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/litho/PlayerFlyoutMenuItemsFilter;" addResources("youtube", "layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch") @@ -51,7 +52,7 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch( SwitchPreference("revanced_hide_player_flyout_lock_screen"), SwitchPreference( key = "revanced_hide_player_flyout_audio_track", - tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference" + tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference", ), SwitchPreference("revanced_hide_player_flyout_watch_in_vr"), SwitchPreference("revanced_hide_player_flyout_sleep_timer"), 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..4f658610f8 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,16 +1,21 @@ package app.revanced.patches.youtube.layout.hide.relatedvideooverlay -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val relatedEndScreenResultsParentFingerprint = fingerprint { - returns("V") - literal{ appRelatedEndScreenResults } +internal val BytecodePatchContext.relatedEndScreenResultsParentMethod by gettingFirstImmutableMethodDeclaratively { + returnType("V") + instructions( + ResourceType.LAYOUT("app_related_endscreen_results"), + ) } -internal val relatedEndScreenResultsFingerprint = fingerprint { - returns("V") - parameters( +context(_: BytecodePatchContext) +internal fun ClassDef.getRelatedEndScreenResultsMethod() = firstMethodDeclaratively { + returnType("V") + parameterTypes( "I", "Z", "I", 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..ad93bc6060 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 @@ -1,35 +1,17 @@ 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.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef 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.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,38 +25,37 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch") PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_related_videos_overlay") + SwitchPreference("revanced_hide_related_videos_overlay"), ) - relatedEndScreenResultsFingerprint.match( - relatedEndScreenResultsParentFingerprint.originalClassDef - ).method.apply { - addInstructionsWithLabels( - 0, - """ - invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideRelatedVideoOverlay()Z - move-result v0 - if-eqz v0, :show - return-void - """, - ExternalLabel("show", getInstruction(0)) - ) - } + val relatedEndScreenResultsMethod = + relatedEndScreenResultsParentMethod.immutableClassDef.getRelatedEndScreenResultsMethod() + + relatedEndScreenResultsMethod.addInstructionsWithLabels( + 0, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideRelatedVideoOverlay()Z + move-result v0 + if-eqz v0, :show + return-void + """, + ExternalLabel("show", relatedEndScreenResultsMethod.getInstruction(0)), + ) } } 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..0372dfb2e0 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 @@ -1,23 +1,24 @@ package app.revanced.patches.youtube.layout.hide.rollingnumber -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.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.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.patches.youtube.shared.rollingNumberTextViewAnimationUpdateFingerprint +import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateMethodMatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisableRollingNumberAnimationsPatch;" -val disableRollingNumberAnimationPatch = bytecodePatch( +@Suppress("unused") +val disableRollingNumberAnimationsPatch = bytecodePatch( name = "Disable rolling number animations", description = "Adds an option to disable rolling number animations of video view count, user likes, and upload time.", ) { @@ -29,14 +30,14 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.hide.rollingnumber.disableRollingNumberAnimationPatch") PreferenceScreen.PLAYER.addPreferences( @@ -45,27 +46,27 @@ val disableRollingNumberAnimationPatch = bytecodePatch( // Animations are disabled by preventing an Image from being applied to the text span, // which prevents the animations from appearing. + rollingNumberTextViewAnimationUpdateMethodMatch.let { + val blockStartIndex = it[0] + val blockEndIndex = it[-1] + 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..8915cac958 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,12 +1,51 @@ package app.revanced.patches.youtube.layout.hide.shorts -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val legacyRenderBottomNavigationBarParentFingerprint = fingerprint { - parameters( +internal val BytecodePatchContext.shortsBottomBarContainerMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("Landroid/view/View;", "Landroid/os/Bundle;") + instructions( + "r_pfvc"(), + ResourceType.ID("bottom_bar_container"), + method("getHeight"), + Opcode.MOVE_RESULT(), + ) +} + +/** + * 19.41 to 20.44. + */ + +context(_: BytecodePatchContext) +internal fun ClassDef.getRenderBottomNavigationBarMethodMatch() = firstMethodDeclaratively { + returnType("V") + parameterTypes("Ljava/lang/String;") + instructions( + Opcode.IGET_OBJECT(), + after(Opcode.MONITOR_ENTER()), + after(Opcode.IGET_OBJECT()), + after(Opcode.IF_EQZ()), + after(Opcode.INVOKE_INTERFACE()), + Opcode.MONITOR_EXIT(), + after(Opcode.RETURN_VOID()), + after(Opcode.MOVE_EXCEPTION()), + after(Opcode.MONITOR_EXIT()), + after(Opcode.THROW()), + ) +} + +/** + * Less than 19.41. + */ +internal val BytecodePatchContext.legacyRenderBottomNavigationBarLegacyParentMethod by gettingFirstImmutableMethodDeclaratively { + parameterTypes( "I", "I", "L", @@ -14,41 +53,18 @@ internal val legacyRenderBottomNavigationBarParentFingerprint = fingerprint { "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 } -} - -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( + "aa"(), ) } /** - * Identical to [legacyRenderBottomNavigationBarParentFingerprint] + * Identical to [legacyRenderBottomNavigationBarLegacyParentMethod] * except this has an extra parameter. */ -internal val renderBottomNavigationBarParentFingerprint = fingerprint { +internal val BytecodePatchContext.renderBottomNavigationBarLegacy1941ParentMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters( + parameterTypes( "I", "I", "L", // ReelWatchEndpointOuterClass @@ -57,38 +73,55 @@ internal val renderBottomNavigationBarParentFingerprint = fingerprint { "Ljava/lang/String;", "L", ) - strings("aa") + instructions( + "aa"(), + ) } -internal val setPivotBarVisibilityFingerprint = fingerprint { +internal val BytecodePatchContext.renderBottomNavigationBarParentMethod by gettingFirstImmutableMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("[Ljava/lang/Class;") + parameterTypes( + "Ljava/lang/Class;", + "Ljava/lang/Object;", + "I", + ) + instructions( + "RPCAC"(), + ) +} + +internal val ClassDef.setPivotBarVisibilityMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") - parameters("Z") + returnType("V") + parameterTypes("Z") opcodes( Opcode.CHECK_CAST, Opcode.IF_EQZ, ) } -internal val setPivotBarVisibilityParentFingerprint = fingerprint { - parameters("Z") - strings("FEnotifications_inbox") +internal val BytecodePatchContext.setPivotBarVisibilityParentMethod by gettingFirstImmutableMethodDeclaratively { + parameterTypes("Z") + instructions( + "FEnotifications_inbox"(), + ) } -internal val shortsExperimentalPlayerFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.shortsExperimentalPlayerFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - literal { - 45677719L - } + returnType("Z") + parameterTypes() + instructions( + 45677719L(), + ) } -internal val renderNextUIFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.renderNextUIFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - literal { - 45649743L - } + returnType("Z") + parameterTypes() + instructions( + 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 2d992cf394..632934da7b 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 @@ -1,53 +1,42 @@ package app.revanced.patches.youtube.layout.hide.shorts -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.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.extensions.wideLiteral +import app.revanced.patcher.firstMethod +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.booleanOption 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.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 import app.revanced.patches.shared.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch 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.versionCheckPatch +import app.revanced.patches.youtube.misc.playservice.* import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -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 app.revanced.util.* 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", + name = "Hide Shorts app shortcut", default = false, - title = "Hide Shorts app shortcut", description = "Permanently hides the shortcut to open Shorts when long pressing the app icon in your launcher.", ) internal val hideShortsWidgetOption = booleanOption( - key = "hideShortsWidget", + name = "Hide Shorts widget", default = false, - title = "Hide Shorts widget", description = "Permanently hides the launcher widget Shorts button.", ) @@ -59,12 +48,77 @@ private val hideShortsComponentsResourcePatch = resourcePatch { versionCheckPatch, ) - execute { + apply { val hideShortsAppShortcut by hideShortsAppShortcutOption val hideShortsWidget by hideShortsWidgetOption 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_live_preview"), + 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_auto_dubbed_label"), + 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,51 +128,7 @@ 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_live_preview"), - 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_auto_dubbed_label"), - 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, ), ) @@ -144,20 +154,10 @@ private val hideShortsComponentsResourcePatch = resourcePatch { shortsItem.removeFromParent() } } - - bottomBarContainer = resourceMappings[ - "id", - "bottom_bar_container", - ] - - reelPlayerRightPivotV2Size = resourceMappings[ - "dimen", - "reel_player_right_pivot_v2_size", - ] } } -private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/ShortsFilter;" +private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/litho/ShortsFilter;" @Suppress("unused") val hideShortsComponentsPatch = bytecodePatch( @@ -175,34 +175,38 @@ 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. + ), ) hideShortsAppShortcutOption() hideShortsWidgetOption() - execute { + apply { addLithoFilter(FILTER_CLASS_DESCRIPTOR) - forEachLiteralValueInstruction( - reelPlayerRightPivotV2Size, - ) { literalInstructionIndex -> - val targetIndex = indexOfFirstInstructionOrThrow(literalInstructionIndex) { - getReference()?.name == "getDimensionPixelSize" + val id = ResourceType.DIMEN["reel_player_right_pivot_v2_size"] + + forEachInstructionAsSequence({ _, method, instruction, index -> + if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null + + val targetIndex = method.indexOfFirstInstructionOrThrow(index) { + methodReference?.name == "getDimensionPixelSize" } + 1 - val sizeRegister = getInstruction(targetIndex).registerA + val sizeRegister = method.getInstruction(targetIndex).registerA - addInstructions( + return@forEachInstructionAsSequence targetIndex to sizeRegister + }) { method, (targetIndex, sizeRegister) -> + firstMethod(method).addInstructions( targetIndex + 1, """ invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I move-result v$sizeRegister - """ + """, ) } @@ -211,49 +215,46 @@ val hideShortsComponentsPatch = bytecodePatch( // region Hide the navigation bar. // Hook to get the pivotBar view. - setPivotBarVisibilityFingerprint.match( - setPivotBarVisibilityParentFingerprint.originalClassDef, - ).let { result -> - result.method.apply { - val insertIndex = result.patternMatch!!.endIndex + setPivotBarVisibilityParentMethod.immutableClassDef.setPivotBarVisibilityMethodMatch.let { match -> + match.method.apply { + val insertIndex = match[-1] val viewRegister = getInstruction(insertIndex - 1).registerA addInstruction( insertIndex, "invoke-static {v$viewRegister}," + - " $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V", + " $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V", ) } } // Hook to hide the shared navigation bar when the Shorts player is opened. - renderBottomNavigationBarFingerprint.match( - if (is_19_41_or_greater) { - renderBottomNavigationBarParentFingerprint - } else { - legacyRenderBottomNavigationBarParentFingerprint - }.originalClassDef, - ).method.addInstruction( - 0, - "invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V", - ) + ( + if (is_20_45_or_greater) { + renderBottomNavigationBarParentMethod + } else if (is_19_41_or_greater) { + renderBottomNavigationBarLegacy1941ParentMethod + } else { + legacyRenderBottomNavigationBarLegacyParentMethod + } + ).immutableClassDef.getRenderBottomNavigationBarMethodMatch().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) + shortsBottomBarContainerMethodMatch.let { + it.method.apply { + val targetIndex = it[-1] + val heightRegister = getInstruction(targetIndex).registerA - 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 - """ - ) + addInstructions( + targetIndex + 1, + """ + invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I + move-result v$heightRegister + """, + ) + } } // endregion @@ -270,12 +271,12 @@ val hideShortsComponentsPatch = bytecodePatch( // Since the buttons are native components and not Litho, it should be possible to // fix the RYD Shorts loading delay by asynchronously loading RYD and updating // the button text after RYD has loaded. - shortsExperimentalPlayerFeatureFlagFingerprint.method.returnLate(false) + shortsExperimentalPlayerFeatureFlagMethod.returnLate(false) // Experimental UI renderer must also be disabled since it requires the // experimental Shorts player. If this is enabled but Shorts player // is disabled then the app crashes when the Shorts player is opened. - renderNextUIFeatureFlagFingerprint.method.returnLate(false) + renderNextUIFeatureFlagMethod.returnLate(false) } // endregion 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..bd5d5eb64f 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 @@ -1,24 +1,20 @@ package app.revanced.patches.youtube.layout.hide.signintotvpopup -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.addInstructionsWithLabels 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;" -val disableSignInToTvPopupPatch = bytecodePatch( +@Suppress("unused") +val disableSignInToTVPopupPatch = bytecodePatch( name = "Disable sign in to TV popup", description = "Adds an option to disable the popup asking to sign into a TV on the same local network.", ) { @@ -26,31 +22,26 @@ val disableSignInToTvPopupPatch = bytecodePatch( settingsPatch, sharedExtensionPatch, addResourcesPatch, - resourceMappingPatch + 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", + ), ) - execute { + apply { addResources("youtube", "layout.hide.signintotv.disableSignInToTvPopupPatch") PreferenceScreen.MISC.addPreferences( 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( + signInToTvPopupMethod.addInstructionsWithLabels( 0, """ invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableSignInToTvPopup()Z @@ -60,7 +51,7 @@ val disableSignInToTvPopupPatch = bytecodePatch( return v0 :allow_sign_in_popup nop - """ + """, ) } } 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..fa3846fdb4 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,14 @@ package app.revanced.patches.youtube.layout.hide.signintotvpopup -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import app.revanced.patches.shared.misc.mapping.ResourceType -internal val signInToTvPopupFingerprint = fingerprint { - returns("Z") - parameters("Ljava/lang/String;", "Z", "L") - literal { - mdx_seamless_tv_sign_in_drawer_fragment_title_id - } -} \ No newline at end of file +internal val BytecodePatchContext.signInToTvPopupMethod by gettingFirstMethodDeclaratively { + returnType("Z") + parameterTypes("Ljava/lang/String;", "Z", "L") + instructions(ResourceType.STRING("mdx_seamless_tv_sign_in_drawer_fragment_title")) +} 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..f641db4142 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,21 @@ package app.revanced.patches.youtube.layout.hide.time -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val timeCounterFingerprint = fingerprint { +internal val BytecodePatchContext.timeCounterMethod by gettingFirstMethodDeclaratively { + returnType("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - opcodes( - Opcode.SUB_LONG_2ADDR, - Opcode.IGET_WIDE, - Opcode.SUB_LONG_2ADDR + parameterTypes() + instructions( + Opcode.SUB_LONG_2ADDR(), + after(allOf(Opcode.INVOKE_STATIC(), method { returnType == "Ljava/lang/CharSequence;" })), + after(Opcode.MOVE_RESULT_OBJECT()), + after(allOf(Opcode.IGET_WIDE(), field { type == "J" })), + after(allOf(Opcode.IGET_WIDE(), field { type == "J" })), + after(Opcode.SUB_LONG_2ADDR()), + afterAtMost(5, allOf(Opcode.INVOKE_STATIC(), method { returnType == "Ljava/lang/CharSequence;" })), ) } 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..24b240232f 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 @@ -1,6 +1,6 @@ package app.revanced.patches.youtube.layout.hide.time -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.addInstructionsWithLabels import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -9,6 +9,9 @@ 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;" + +@Suppress("unused") val hideTimestampPatch = bytecodePatch( name = "Hide timestamp", description = "Adds an option to hide the timestamp in the bottom left of the video player.", @@ -21,24 +24,24 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.hide.time.hideTimestampPatch") PreferenceScreen.SEEKBAR.addPreferences( SwitchPreference("revanced_hide_timestamp"), ) - timeCounterFingerprint.method.addInstructionsWithLabels( + timeCounterMethod.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 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..796a308636 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,40 +2,16 @@ package app.revanced.patches.youtube.layout.miniplayer -import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.ClassDefComposing +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode - -internal val miniplayerDimensionsCalculatorParentFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - literal { floatyBarButtonTopMargin } -} - -/** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. - */ -internal val miniplayerModernAddViewListenerFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Landroid/view/View;") -} - -/** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. - */ - -internal val miniplayerModernCloseButtonFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() - literal { modernMiniplayerClose } -} +import com.android.tools.smali.dexlib2.iface.ClassDef 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 @@ -44,107 +20,167 @@ 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 { +internal val BytecodePatchContext.miniplayerModernConstructorMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("L") - literal { 45623000L } + instructions( + 45623000L(), // Magic number found in the constructor. + ) } -internal val miniplayerOnCloseHandlerFingerprint = fingerprint { +internal val BytecodePatchContext.miniplayerDimensionsCalculatorParentMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - literal { MINIPLAYER_DISABLED_FEATURE_KEY } + returnType("V") + parameterTypes("L") + instructions( + ResourceType.DIMEN("floaty_bar_button_top_margin"), + ) +} + +internal val BytecodePatchContext.miniplayerModernViewParentMethod by gettingFirstImmutableMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Ljava/lang/String;") + parameterTypes() + instructions( + "player_overlay_modern_mini_player_controls"(), + ) } /** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. + * Matches using the class found in [miniplayerModernViewParentMethod]. */ -internal val miniplayerModernExpandButtonFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getMiniplayerModernAddViewListenerMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() - literal { modernMiniplayerExpand } + returnType("V") + parameterTypes("Landroid/view/View;") } /** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. + * Matches using the class found in [miniplayerModernViewParentMethod]. */ -internal val miniplayerModernExpandCloseDrawablesFingerprint = fingerprint { +internal val ClassDef.miniplayerModernCloseButtonMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - literal { ytOutlinePictureInPictureWhite24 } + returnType("L") + parameterTypes() + instructions( + ResourceType.ID("modern_miniplayer_close"), + allOf(Opcode.CHECK_CAST(), type("Landroid/widget/ImageView;")), + ) } /** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. + * Matches using the class found in [miniplayerModernViewParentMethod]. */ -internal val miniplayerModernForwardButtonFingerprint = fingerprint { +internal val ClassDef.miniplayerModernExpandButtonMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() - literal { modernMiniplayerForwardButton } + returnType("L") + parameterTypes() + instructions( + ResourceType.ID("modern_miniplayer_expand"), + allOf(Opcode.CHECK_CAST(), type("Landroid/widget/ImageView;")), + ) } /** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. + * Matches using the class found in [miniplayerModernViewParentMethod]. */ -internal val miniplayerModernOverlayViewFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getMiniplayerModernExpandCloseDrawablesMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters() - literal { scrimOverlay } + returnType("V") + parameterTypes("L") + instructions( + ytOutlinePictureInPictureWhite24(), + ) } /** - * Matches using the class found in [miniplayerModernViewParentFingerprint]. + * Matches using the class found in [miniplayerModernViewParentMethod]. */ -internal val miniplayerModernRewindButtonFingerprint = fingerprint { +internal val ClassDef.miniplayerModernForwardButtonMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() - literal { modernMiniplayerRewindButton } + returnType("L") + parameterTypes() + instructions( + ResourceType.ID("modern_miniplayer_forward_button"), + afterAtMost(5, Opcode.MOVE_RESULT_OBJECT()), + ) } -internal val miniplayerModernViewParentFingerprint = fingerprint { +internal val ClassDef.miniplayerModernOverlayViewMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters() - strings("player_overlay_modern_mini_player_controls") + parameterTypes() + instructions( + ResourceType.ID("scrim_overlay"), + afterAtMost(5, Opcode.MOVE_RESULT_OBJECT()), + ) } -internal val miniplayerModernActionButtonFingerprint = fingerprint { +/** + * Matches using the class found in [miniplayerModernViewParentMethod]. + */ +internal val ClassDef.miniplayerModernRewindButtonMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() - literal { modernMiniPlayerOverlayActionButton } + returnType("L") + parameterTypes() + instructions( + ResourceType.ID("modern_miniplayer_rewind_button"), + afterAtMost(5, Opcode.MOVE_RESULT_OBJECT()), + ) } -internal val miniplayerMinimumSizeFingerprint = fingerprint { +/** + * Matches using the class found in [miniplayerModernViewParentMethod]. + */ +internal val ClassDef.miniplayerModernActionButtonMethodMatch by ClassDefComposing.composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("L") + parameterTypes() + instructions( + ResourceType.ID("modern_miniplayer_overlay_action_button"), + afterAtMost(5, Opcode.MOVE_RESULT_OBJECT()), + ) +} + +internal val BytecodePatchContext.miniplayerMinimumSizeMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - custom { method, _ -> - method.containsLiteralInstruction(192) && - method.containsLiteralInstruction(128) && - method.containsLiteralInstruction(miniplayerMaxSize) - } + instructions( + ResourceType.DIMEN("miniplayer_max_size"), + 192L(), // Default miniplayer width constant. + 128L(), // Default miniplayer height constant. + ) } -internal val miniplayerOverrideFingerprint = fingerprint { +internal val BytecodePatchContext.miniplayerOverrideMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - strings("appName") + returnType("L") + instructions( + "appName"(), + afterAtMost( + 10, + method { parameterTypes.count() == 1 && parameterTypes.first() == "Landroid/content/Context;" && returnType == "Z" }, + ), + ) } -internal val miniplayerOverrideNoContextFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getMiniplayerOverrideNoContextMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("Z") - opcodes(Opcode.IGET_BOOLEAN) // Anchor to insert the instruction. + returnType("Z") + opcodes( + Opcode.IGET_BOOLEAN, // Anchor to insert the instruction. + ) } -internal val miniplayerResponseModelSizeCheckFingerprint = fingerprint { +/** + * 20.36 and lower. Codes appears to be removed in 20.37+ + */ +internal val BytecodePatchContext.miniplayerResponseModelSizeCheckMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters("Ljava/lang/Object;", "Ljava/lang/Object;") + returnType("L") + parameterTypes("Ljava/lang/Object;", "Ljava/lang/Object;") opcodes( Opcode.RETURN_OBJECT, Opcode.CHECK_CAST, @@ -155,11 +191,26 @@ internal val miniplayerResponseModelSizeCheckFingerprint = fingerprint { ) } +internal val BytecodePatchContext.miniplayerOnCloseHandlerMethod by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Z") + instructions( + MINIPLAYER_DISABLED_FEATURE_KEY(), + ) +} + internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME = "Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;" -internal val playerOverlaysLayoutFingerprint = fingerprint { - custom { method, _ -> - method.definingClass == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME - } +internal val BytecodePatchContext.playerOverlaysLayoutMethod by gettingFirstImmutableMethodDeclaratively { + definingClass(YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME) +} + +internal val BytecodePatchContext.miniplayerSetIconsMethod by gettingFirstMethodDeclaratively { + returnType("V") + parameterTypes("I", "Ljava/lang/Runnable;") + instructions( + ResourceType.DRAWABLE("yt_fill_pause_white_36"), + 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..a2effc6a04 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 @@ -2,20 +2,20 @@ package app.revanced.patches.youtube.layout.miniplayer -import app.revanced.patcher.Fingerprint -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.replaceInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.immutableClassDef 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.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.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.* @@ -25,40 +25,20 @@ import app.revanced.util.* 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.ClassDef 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( @@ -66,73 +46,15 @@ private val miniplayerResourcePatch = resourcePatch { versionCheckPatch, ) - 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" - ] - + apply { // Resource id is not used during patching, but is used by extension. // Verify the resource is present while patching. - resourceMappings[ - "id", - "modern_miniplayer_subtitle_text", - ] + ResourceType.ID["modern_miniplayer_subtitle_text"] // Only required for exactly 19.16 if (!is_19_17_or_greater) { - ytOutlinePictureInPictureWhite24 = resourceMappings[ - "drawable", - "yt_outline_picture_in_picture_white_24", - ] - - ytOutlineXWhite24 = resourceMappings[ - "drawable", - "yt_outline_x_white_24", - ] - } - - if (is_19_26_or_greater) { - miniplayerMaxSize = resourceMappings[ - "dimen", - "miniplayer_max_size", - ] + ytOutlinePictureInPictureWhite24 = ResourceType.DRAWABLE["yt_outline_picture_in_picture_white_24"] + ytOutlineXWhite24 = ResourceType.DRAWABLE["yt_outline_x_white_24"] } } } @@ -142,7 +64,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/ @Suppress("unused") val miniplayerPatch = bytecodePatch( name = "Miniplayer", - description = "Adds options to change the in-app minimized player." + description = "Adds options to change the in-app minimized player.", ) { dependsOn( sharedExtensionPatch, @@ -153,32 +75,38 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.miniplayer.miniplayerPatch") 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", entriesKey = "revanced_miniplayer_type_legacy_19_43_entries", - entryValuesKey = "revanced_miniplayer_type_legacy_19_43_entry_values" + entryValuesKey = "revanced_miniplayer_type_legacy_19_43_entry_values", ) } else { ListPreference( key = "revanced_miniplayer_type", entriesKey = "revanced_miniplayer_type_legacy_19_16_entries", - entryValuesKey = "revanced_miniplayer_type_legacy_19_16_entry_values" + entryValuesKey = "revanced_miniplayer_type_legacy_19_16_entry_values", ) } @@ -235,7 +163,7 @@ val miniplayerPatch = bytecodePatch( """ invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z move-result v$register - """ + """, ) } @@ -255,31 +183,29 @@ val miniplayerPatch = bytecodePatch( insertMiniplayerBooleanOverride(index, "getModernMiniplayerOverride") } - fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride( + fun MutableMethod.insertMiniplayerFeatureFlagBooleanOverride( literal: Long, extensionMethod: String, - ) = method.insertLiteralOverride( + ) = insertLiteralOverride( literal, - "$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z" + "$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z", ) - fun Fingerprint.insertMiniplayerFeatureFlagFloatOverride( + fun MutableMethod.insertMiniplayerFeatureFlagFloatOverride( literal: Long, extensionMethod: String, - ) { - method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) - val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT) - val register = getInstruction(targetIndex).registerA + ) = apply { + val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) + val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT) + val register = getInstruction(targetIndex).registerA - addInstructions( - targetIndex + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F - move-result v$register - """, - ) - } + addInstructions( + targetIndex + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F + move-result v$register + """, + ) } /** @@ -297,54 +223,40 @@ val miniplayerPatch = bytecodePatch( ) } - 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) } - } - - // 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;" + if (!is_20_37_or_greater) { + miniplayerDimensionsCalculatorParentMethod.immutableClassDef.getMiniplayerOverrideNoContextMethod().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) + // endregion + + // region Legacy tablet miniplayer hooks. + miniplayerOverrideMethodMatch.let { + val appNameStringIndex = it[-1] + navigate(it.immutableMethod).to(appNameStringIndex).stop().apply { + findReturnIndicesReversed().forEach { index -> + insertLegacyTabletMiniplayerOverride( + index, + ) + } + } + } + + miniplayerResponseModelSizeCheckMethodMatch.let { + it.method.insertLegacyTabletMiniplayerOverride(it[-1]) + } } // endregion // region Enable modern miniplayer. - miniplayerModernConstructorFingerprint.classDef.methods.forEach { + miniplayerModernConstructorMethod.classDef.methods.forEach { it.apply { if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { val iPutIndex = indexOfFirstInstructionOrThrow { @@ -359,36 +271,35 @@ val miniplayerPatch = bytecodePatch( } if (is_19_23_or_greater) { - miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_DRAG_DROP_FEATURE_KEY, "getMiniplayerDragAndDrop", ) } if (is_19_25_or_greater) { - miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_MODERN_FEATURE_LEGACY_KEY, "getModernMiniplayerOverride", ) - miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_MODERN_FEATURE_KEY, "getModernFeatureFlagsActiveOverride", ) - miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_DOUBLE_TAP_FEATURE_KEY, "getMiniplayerDoubleTapAction", ) } if (is_19_26_or_greater) { - miniplayerModernConstructorFingerprint.method.apply { + miniplayerModernConstructorMethod.apply { val literalIndex = indexOfFirstLiteralInstructionOrThrow( MINIPLAYER_INITIAL_SIZE_FEATURE_KEY, ) val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT) - val register = getInstruction(targetIndex).registerA addInstructions( @@ -401,36 +312,41 @@ val miniplayerPatch = bytecodePatch( } // Override a minimum size constant. - miniplayerMinimumSizeFingerprint.method.apply { - val index = indexOfFirstInstructionOrThrow { - opcode == Opcode.CONST_16 && (this as NarrowLiteralInstruction).narrowLiteral == 192 - } - val register = getInstruction(index).registerA + miniplayerMinimumSizeMethodMatch.let { + it.method.apply { + val index = it[1] + 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") + // 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") + } } } if (is_19_36_or_greater) { - miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY, "getRoundedCorners", ) } if (is_19_43_or_greater) { - miniplayerOnCloseHandlerFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + miniplayerOnCloseHandlerMethod.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_DISABLED_FEATURE_KEY, - "getMiniplayerOnCloseHandler" + "getMiniplayerOnCloseHandler", ) - miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( + miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY, "getHorizontalDrag", ) + + miniplayerModernConstructorMethod.insertMiniplayerFeatureFlagBooleanOverride( + MINIPLAYER_ANIMATED_EXPAND_FEATURE_KEY, + "getMaximizeAnimation", + ) } // endregion @@ -438,10 +354,8 @@ 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) { - miniplayerModernExpandCloseDrawablesFingerprint.match( - miniplayerModernViewParentFingerprint.originalClassDef, - ).method.apply { + if (!is_19_17_or_greater) { + miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernExpandCloseDrawablesMethod().apply { listOf( ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24, ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24, @@ -456,65 +370,54 @@ val miniplayerPatch = bytecodePatch( // endregion + // region fix minimal miniplayer using the wrong pause/play bold icons. + + if (is_20_31_or_greater) { + miniplayerSetIconsMethod.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) -> - fingerprint.match( - miniplayerModernViewParentFingerprint.originalClassDef - ).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 + ClassDef::miniplayerModernExpandButtonMethodMatch to "hideMiniplayerExpandClose", + ClassDef::miniplayerModernCloseButtonMethodMatch to "hideMiniplayerExpandClose", + ClassDef::miniplayerModernActionButtonMethodMatch to "hideMiniplayerActionButton", + ClassDef::miniplayerModernRewindButtonMethodMatch to "hideMiniplayerRewindForward", + ClassDef::miniplayerModernForwardButtonMethodMatch to "hideMiniplayerRewindForward", + ClassDef::miniplayerModernOverlayViewMethodMatch to "adjustMiniplayerOpacity", + ).forEach { (matching, methodName) -> + val match = matching.get(miniplayerModernViewParentMethod.immutableClassDef) + + match.method.apply { + val index = match[-1] + 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", ) } } - miniplayerModernAddViewListenerFingerprint.match( - miniplayerModernViewParentFingerprint.classDef, - ).method.addInstruction( + miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernAddViewListenerMethod().addInstruction( 0, "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->" + "hideMiniplayerSubTexts(Landroid/view/View;)V", @@ -529,7 +432,7 @@ val miniplayerPatch = bytecodePatch( // NOTE: Modern 2 uses the same video UI as the regular player except resized to smaller. // This patch code could be used to hide other player overlays that do not use Litho. if (!is_19_17_or_greater) { - playerOverlaysLayoutFingerprint.classDef.methods.add( + playerOverlaysLayoutMethod.classDef.methods.add( ImmutableMethod( YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME, "addView", @@ -539,7 +442,7 @@ val miniplayerPatch = bytecodePatch( ImmutableMethodParameter( "Landroid/view/ViewGroup\$LayoutParams;", null, - null + null, ), ), "V", @@ -550,12 +453,12 @@ 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..28c218d9d1 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,13 +1,12 @@ package app.revanced.patches.youtube.layout.panels.popup -import com.android.tools.smali.dexlib2.AccessFlags -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val engagementPanelControllerFingerprint = fingerprint { - accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("L") - strings( - "EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.", - "[EngagementPanel] Cannot show EngagementPanel before EngagementPanelController.init() has been called.", - ) +internal val BytecodePatchContext.engagementPanelControllerMethod by gettingFirstMethodDeclaratively( + "EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.", + "[EngagementPanel] Cannot show EngagementPanel before EngagementPanelController.init() has been called.", +) { + returnType("L") } 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..6e952f4461 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 @@ -1,6 +1,6 @@ package app.revanced.patches.youtube.layout.panels.popup -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.addInstructionsWithLabels import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -9,7 +9,10 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -val playerPopupPanelsPatch = bytecodePatch( +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisablePlayerPopupPanelsPatch;" + +@Suppress("unused") +val disablePlayerPopupPanelsPatch = bytecodePatch( name = "Disable player popup panels", description = "Adds an option to disable panels (such as live chat) from opening automatically.", ) { @@ -21,24 +24,24 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.panels.popup.playerPopupPanelsPatch") PreferenceScreen.PLAYER.addPreferences( SwitchPreference("revanced_hide_player_popup_panels"), ) - engagementPanelControllerFingerprint.method.addInstructionsWithLabels( + engagementPanelControllerMethod.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 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..9f04df3c06 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,23 +9,24 @@ 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( - name = "Exit fullscreen mode", - description = "Adds options to automatically exit fullscreen mode when a video reaches the end." +val exitFullscreenPatch = bytecodePatch( + name = "Exit fullscreen", + description = "Adds options to automatically exit fullscreen mode when a video reaches the end.", ) { - 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", + ), ) dependsOn( @@ -33,7 +34,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 @@ -42,16 +44,18 @@ internal val exitFullscreenPatch = bytecodePatch( val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ExitFullscreenPatch;" - execute { + apply { addResources("youtube", "layout.player.fullscreen.exitFullscreenPatch") PreferenceScreen.PLAYER.addPreferences( - ListPreference("revanced_exit_fullscreen") + 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..d6370b2aa7 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,25 +1,50 @@ package app.revanced.patches.youtube.layout.player.fullscreen -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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 BytecodePatchContext.openVideosFullscreenPortraitMethodMatch by composingFirstMethod { + returnType("V") + parameterTypes("L", "Lj\$/util/Optional;") + instructions( + Opcode.MOVE_RESULT(), // Conditional check to modify. + // Open videos fullscreen portrait feature flag. + afterAtMost(5, 45666112L()), // Cannot be more than 5. + afterAtMost(10, Opcode.MOVE_RESULT()), + ) +} -internal val openVideosFullscreenPortraitFingerprint = fingerprint { +/** + * Pre 19.46. + */ +internal val BytecodePatchContext.openVideosFullscreenPortraitLegacyMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L", "Lj\$/util/Optional;") - literal { - OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG - } + returnType("V") + parameterTypes("L", "Lj\$/util/Optional;") + 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 { +internal val BytecodePatchContext.openVideosFullscreenHookPatchExtensionMethod by gettingFirstMethodDeclaratively { + name("isFullScreenPatchIncluded") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("Z") - parameters() - custom { methodDef, classDef -> - methodDef.name == "isFullScreenPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Z") + parameterTypes() } 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..5f53f6cb7a 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.CompositeMatch +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.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;" @@ -13,20 +17,46 @@ internal const val EXTENSION_CLASS_DESCRIPTOR = /** * Used by both [openVideosFullscreenPatch] and [openShortsInRegularPlayerPatch]. */ + internal val openVideosFullscreenHookPatch = bytecodePatch { dependsOn( sharedExtensionPatch, - versionCheckPatch + versionCheckPatch, ) - execute { - if (!is_19_46_or_greater) { - return@execute + apply { + var match: CompositeMatch + var insertIndex: Int + + if (is_19_46_or_greater) { + match = openVideosFullscreenPortraitMethodMatch + insertIndex = match[0] + + openVideosFullscreenPortraitMethodMatch.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[-1], + false, + ) + } + } else { + match = openVideosFullscreenPortraitLegacyMethodMatch + insertIndex = match[-1] } - openVideosFullscreenPortraitFingerprint.method.insertLiteralOverride( - OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG, - "$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z" - ) + match.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..c1a70e42d4 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,29 +17,27 @@ 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") - } - + apply { addResources("youtube", "layout.player.fullscreen.openVideosFullscreen") PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_open_videos_fullscreen_portrait") + SwitchPreference("revanced_open_videos_fullscreen_portrait"), ) // Enable the logic for the user Setting to open regular videos fullscreen. - openVideosFullscreenHookPatchExtensionFingerprint.method.returnEarly(true) + openVideosFullscreenHookPatchExtensionMethod.returnEarly(true) } -} \ No newline at end of file +} 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..4bb4c275d0 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 @@ -1,45 +1,17 @@ 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.extensions.addInstruction +import app.revanced.patcher.extensions.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,39 @@ 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 + apply { + addResources("youtube", "layout.player.overlay.customPlayerOverlayOpacityResourcePatch") - val insertIndex = viewRegisterIndex + 1 - addInstruction( - insertIndex, - "invoke-static { v$viewRegister }, " + - "$EXTENSION_CLASS_DESCRIPTOR->changeOpacity(Landroid/widget/ImageView;)V", - ) + PreferenceScreen.PLAYER.addPreferences( + TextPreference("revanced_player_overlay_opacity", inputType = InputType.NUMBER), + ) + + createPlayerOverviewMethodMatch.let { + it.method.apply { + val viewRegisterIndex = it[-1] + 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..7756cda084 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,14 @@ package app.revanced.patches.youtube.layout.player.overlay -import app.revanced.patcher.fingerprint -import app.revanced.util.literal -import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.Opcode -internal val createPlayerOverviewFingerprint = fingerprint { - accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") - opcodes( - Opcode.CONST, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CHECK_CAST, +internal val BytecodePatchContext.createPlayerOverviewMethodMatch by composingFirstMethod { + returnType("V") + instructions( + ResourceType.ID("scrim_overlay"), + afterAtMost(10, allOf(Opcode.CHECK_CAST(), type("Landroid/widget/ImageView;"))), ) - 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..7588c8d899 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,31 +1,33 @@ package app.revanced.patches.youtube.layout.returnyoutubedislike -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val dislikeFingerprint = fingerprint { - returns("V") - strings("like/dislike") +internal val BytecodePatchContext.dislikeMethod by gettingFirstMethodDeclaratively { + returnType("V") + instructions("like/dislike"()) } -internal val likeFingerprint = fingerprint { - returns("V") - strings("like/like") +internal val BytecodePatchContext.likeMethod by gettingFirstMethodDeclaratively { + returnType("V") + instructions("like/like"()) } -internal val removeLikeFingerprint = fingerprint { - returns("V") - strings("like/removelike") +internal val BytecodePatchContext.removeLikeMethod by gettingFirstMethodDeclaratively { + returnType("V") + instructions("like/removelike"()) } -internal val rollingNumberMeasureAnimatedTextFingerprint = fingerprint { +internal val BytecodePatchContext.rollingNumberMeasureAnimatedTextMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Lj\$/util/Optional;") - parameters("L", "Ljava/lang/String;", "L") + returnType("Lj\$/util/Optional;") + parameterTypes("L", "Ljava/lang/String;", "L") opcodes( - Opcode.IGET, // First instruction of method + Opcode.IGET, // First instruction of method. Opcode.IGET_OBJECT, Opcode.IGET_OBJECT, Opcode.CONST_HIGH16, @@ -34,17 +36,17 @@ internal val rollingNumberMeasureAnimatedTextFingerprint = fingerprint { Opcode.CONST_4, Opcode.AGET, Opcode.CONST_4, - Opcode.CONST_4, // Measured text width + Opcode.CONST_4, // Measured text width. ) } /** - * Matches to class found in [rollingNumberMeasureStaticLabelParentFingerprint]. + * Matches to class found in [rollingNumberMeasureStaticLabelParentMethod]. */ -internal val rollingNumberMeasureStaticLabelFingerprint = fingerprint { +internal val ClassDef.rollingNumberMeasureStaticLabelMethodMatch by ClassDefComposing.composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("F") - parameters("Ljava/lang/String;") + returnType("F") + parameterTypes("Ljava/lang/String;") opcodes( Opcode.IGET_OBJECT, Opcode.INVOKE_VIRTUAL, @@ -53,69 +55,73 @@ internal val rollingNumberMeasureStaticLabelFingerprint = fingerprint { ) } -internal val rollingNumberMeasureStaticLabelParentFingerprint = fingerprint { +internal val BytecodePatchContext.rollingNumberMeasureStaticLabelParentMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters() - strings("RollingNumberFontProperties{paint=") + returnType("Ljava/lang/String;") + parameterTypes() + instructions( + "RollingNumberFontProperties{paint="(), + ) } -internal val rollingNumberSetterFingerprint = fingerprint { +internal val BytecodePatchContext.rollingNumberSetterMethodMatch by composingFirstMethod { opcodes( Opcode.INVOKE_DIRECT, Opcode.IGET_OBJECT, ) - // Partial string match. - strings("RollingNumberType required properties missing! Need") + + val match = indexedMatcher("RollingNumberType required properties missing! Need"(String::contains)) + custom { match(instructions) } } -internal val rollingNumberTextViewFingerprint = fingerprint { +internal val BytecodePatchContext.rollingNumberTextViewMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L", "F", "F") - opcodes( - Opcode.IPUT, - null, // invoke-direct or invoke-virtual - Opcode.IPUT_OBJECT, - Opcode.IGET_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.RETURN_VOID, + returnType("V") + parameterTypes("L", "F", "F") + instructions( + Opcode.IPUT(), + after(anyOf(Opcode.INVOKE_DIRECT(), Opcode.INVOKE_VIRTUAL())), + after(Opcode.IPUT_OBJECT()), + after(Opcode.IGET_OBJECT()), + after(Opcode.INVOKE_VIRTUAL()), + after(Opcode.RETURN_VOID()), ) - custom { _, classDef -> - classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" || classDef.superclass == - "Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;" + custom { + immutableClassDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" || immutableClassDef.superclass == + "Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;" } } -internal val textComponentConstructorFingerprint = fingerprint { +internal val BytecodePatchContext.textComponentConstructorMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PRIVATE) - strings("TextComponent") + instructions( + "TextComponent"(), + ) } -internal val textComponentDataFingerprint = fingerprint { +internal val BytecodePatchContext.textComponentDataMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("L", "L") - strings("text") - custom { _, classDef -> - classDef.fields.find { it.type == "Ljava/util/BitSet;" } != null - } + parameterTypes("L", "L") + instructions( + "text"(), + ) + custom { immutableClassDef.anyField { type == "Ljava/util/BitSet;" } } } /** - * Matches against the same class found in [textComponentConstructorFingerprint]. + * Matches against the same class found in [textComponentConstructorMethod]. */ -internal val textComponentLookupFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getTextComponentLookupMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) - returns("L") - parameters("L") - strings("…") + returnType("L") + parameterTypes("L") + instructions("…"()) } -internal const val LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG = 45675738L - -internal val textComponentFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.textComponentFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.FINAL) - returns("Z") - parameters() - literal { LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG } + returnType("Z") + parameterTypes() + instructions(45675738L()) } 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 50db7c281e..bb81a31c0c 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 @@ -1,7 +1,11 @@ package app.revanced.patches.youtube.layout.returnyoutubedislike -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.fieldReference +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.extensions.typeReference +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources @@ -10,24 +14,20 @@ 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.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.shared.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch 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.versionCheckPatch +import app.revanced.patches.youtube.misc.playservice.* import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.patches.youtube.shared.conversionContextFingerprintToString -import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateFingerprint +import app.revanced.patches.youtube.shared.conversionContextToStringMethod +import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateMethodMatch import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId import app.revanced.patches.youtube.video.videoid.hookVideoId import app.revanced.patches.youtube.video.videoid.videoIdPatch import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findFreeRegister -import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.returnLate import com.android.tools.smali.dexlib2.Opcode @@ -35,16 +35,15 @@ 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.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;" private const val FILTER_CLASS_DESCRIPTOR = - "Lapp/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter;" + "Lapp/revanced/extension/youtube/patches/litho/ReturnYouTubeDislikeFilter;" +@Suppress("ObjectPropertyName") val returnYouTubeDislikePatch = bytecodePatch( name = "Return YouTube Dislike", description = "Adds an option to show the dislike count of videos with Return YouTube Dislike.", @@ -61,14 +60,15 @@ 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. + ), ) - execute { + apply { addResources("youtube", "layout.returnyoutubedislike.returnYouTubeDislikePatch") PreferenceScreen.RETURN_YOUTUBE_DISLIKE.addPreferences( @@ -87,8 +87,8 @@ val returnYouTubeDislikePatch = bytecodePatch( key = "revanced_ryd_statistics_category", sorting = PreferenceScreenPreference.Sorting.UNSORTED, preferences = emptySet(), // Preferences are added by custom class at runtime. - tag = "app.revanced.extension.youtube.returnyoutubedislike.ui.ReturnYouTubeDislikeDebugStatsPreferenceCategory" - ) + tag = "app.revanced.extension.youtube.returnyoutubedislike.ui.ReturnYouTubeDislikeDebugStatsPreferenceCategory", + ), ) // region Inject newVideoLoaded event handler to update dislikes when a new video is loaded. @@ -102,12 +102,12 @@ val returnYouTubeDislikePatch = bytecodePatch( // region Hook like/dislike/remove like button clicks to send votes to the API. - mapOf( - likeFingerprint to Vote.LIKE, - dislikeFingerprint to Vote.DISLIKE, - removeLikeFingerprint to Vote.REMOVE_LIKE, - ).forEach { (fingerprint, vote) -> - fingerprint.method.addInstructions( + arrayOf( + likeMethod to Vote.LIKE, + dislikeMethod to Vote.DISLIKE, + removeLikeMethod to Vote.REMOVE_LIKE, + ).forEach { (method, vote) -> + method.addInstructions( 0, """ const/4 v0, ${vote.value} @@ -120,64 +120,65 @@ 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 = conversionContextToStringMethod.immutableClassDef + val textComponentConversionContextField = textComponentConstructorMethod.immutableClassDef.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 { + textComponentConstructorMethod.immutableClassDef.getTextComponentLookupMethod().apply { // Find the instruction for creating the text data object. - val textDataClassType = textComponentDataFingerprint.originalClassDef.type + val textDataClassType = textComponentDataMethod.immutableClassDef.type val insertIndex: Int - val tempRegister: Int val charSequenceRegister: Int - if (is_19_33_or_greater && !is_20_10_or_greater) { - insertIndex = indexOfFirstInstructionOrThrow { - (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) - && getReference()?.returnType == textDataClassType + if (is_19_33_or_greater && !is_20_10_or_greater) { + val index = indexOfFirstInstructionOrThrow { + (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) && + methodReference?.returnType == textDataClassType } - tempRegister = getInstruction(insertIndex + 1).registerA + insertIndex = indexOfFirstInstructionOrThrow(index) { + opcode == Opcode.INVOKE_VIRTUAL && + methodReference?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;" + } - // 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 && - getReference()?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;" - }, - ).registerD + charSequenceRegister = getInstruction(insertIndex).registerD } else { insertIndex = indexOfFirstInstructionOrThrow { opcode == Opcode.NEW_INSTANCE && - getReference()?.type == textDataClassType + typeReference?.type == textDataClassType } - tempRegister = getInstruction(insertIndex).registerA - - charSequenceRegister = getInstruction( - indexOfFirstInstructionOrThrow(insertIndex) { - opcode == Opcode.IPUT_OBJECT && - getReference()?.type == "Ljava/lang/CharSequence;" - }, - ).registerA + val charSequenceIndex = indexOfFirstInstructionOrThrow(insertIndex) { + opcode == Opcode.IPUT_OBJECT && + fieldReference?.type == "Ljava/lang/CharSequence;" + } + charSequenceRegister = getInstruction(charSequenceIndex).registerA } + val conversionContext = findFreeRegister(insertIndex, charSequenceRegister) + 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$conversionContext, p0 + + iget-object v$conversionContext, v$conversionContext, $textComponentConversionContextField + + invoke-static { v$conversionContext, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; move-result-object v$charSequenceRegister + + :ignore + nop """ ) } @@ -194,7 +195,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 { + textComponentFeatureFlagMethod.returnLate(false) + } } // Player response video id is needed to search for the video ids in Shorts litho components. @@ -204,15 +220,11 @@ val returnYouTubeDislikePatch = bytecodePatch( // region Hook rolling numbers. - val dislikesIndex = rollingNumberSetterFingerprint.patternMatch!!.endIndex - - rollingNumberSetterFingerprint.method.apply { + rollingNumberSetterMethodMatch.method.apply { val insertIndex = 1 - - val charSequenceInstanceRegister = - getInstruction(0).registerA - val charSequenceFieldReference = - getInstruction(dislikesIndex).reference + val dislikesIndex = rollingNumberSetterMethodMatch[-1] + val charSequenceInstanceRegister = getInstruction(0).registerA + val charSequenceFieldReference = getInstruction(dislikesIndex).reference val conversionContextRegister = implementation!!.registerCount - parameters.size + 1 @@ -229,13 +241,12 @@ val returnYouTubeDislikePatch = bytecodePatch( ) } - 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 + // 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. + rollingNumberMeasureAnimatedTextMethodMatch.let { + // Additional check to verify the opcodes are at the start of the method + if (it[0] != 0) throw PatchException("Unexpected opcode location") + val endIndex = it[-1] it.method.apply { val measuredTextWidthRegister = getInstruction(endIndex).registerA @@ -245,17 +256,15 @@ val returnYouTubeDislikePatch = bytecodePatch( """ invoke-static {p1, v$measuredTextWidthRegister}, $EXTENSION_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F move-result v$measuredTextWidthRegister - """ + """, ) } } // Additional text measurement method. Used if YouTube decides not to animate the likes count // and sometimes used for initial video load. - rollingNumberMeasureStaticLabelFingerprint.match( - rollingNumberMeasureStaticLabelParentFingerprint.originalClassDef, - ).let { - val measureTextIndex = it.patternMatch!!.startIndex + 1 + rollingNumberMeasureStaticLabelParentMethod.immutableClassDef.rollingNumberMeasureStaticLabelMethodMatch.let { + val measureTextIndex = it[0] + 1 it.method.apply { val freeRegister = getInstruction(0).registerA @@ -273,27 +282,25 @@ val returnYouTubeDislikePatch = bytecodePatch( // The rolling number Span is missing styling since it's initially set as a String. // Modify the UI text view and use the styled like/dislike Span. // Initial TextView is set in this method. - rollingNumberTextViewFingerprint.method, + rollingNumberTextViewMethod, // Videos less than 24 hours after uploaded, like counts will be updated in real time. // Whenever like counts are updated, TextView is set in this method. - rollingNumberTextViewAnimationUpdateFingerprint.method, + rollingNumberTextViewAnimationUpdateMethodMatch.method, ).forEach { insertMethod -> insertMethod.apply { val setTextIndex = indexOfFirstInstructionOrThrow { - getReference()?.name == "setText" + methodReference?.name == "setText" } - val textViewRegister = - getInstruction(setTextIndex).registerC - val textSpanRegister = - getInstruction(setTextIndex).registerD + val textViewRegister = getInstruction(setTextIndex).registerC + val textSpanRegister = getInstruction(setTextIndex).registerD addInstructions( setTextIndex, """ invoke-static {v$textViewRegister, v$textSpanRegister}, $EXTENSION_CLASS_DESCRIPTOR->updateRollingNumber(Landroid/widget/TextView;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; move-result-object v$textSpanRegister - """ + """, ) } } 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..e7748db661 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,27 +1,34 @@ package app.revanced.patches.youtube.layout.searchbar -import app.revanced.patcher.fingerprint -import app.revanced.patches.youtube.layout.hide.general.yoodlesImageViewFingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.literal +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.youtube.layout.hide.general.yoodlesImageViewMethod import com.android.tools.smali.dexlib2.AccessFlags -internal val setWordmarkHeaderFingerprint = fingerprint { +internal val BytecodePatchContext.setWordmarkHeaderMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Landroid/widget/ImageView;") - custom { methodDef, _ -> - methodDef.containsLiteralInstruction(ytPremiumWordmarkHeaderId) && - methodDef.containsLiteralInstruction(ytWordmarkHeaderId) - } + returnType("V") + parameterTypes("Landroid/widget/ImageView;") + instructions( + ResourceType.ATTR("ytPremiumWordmarkHeader"), + ResourceType.ATTR("ytWordmarkHeader"), + ) } /** - * Matches the same method as [yoodlesImageViewFingerprint]. + * Matches the same method as [yoodlesImageViewMethod]. */ -internal val wideSearchbarLayoutFingerprint = fingerprint { +internal val BytecodePatchContext.wideSearchbarLayoutMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/view/View;") - parameters("L", "L") - literal { actionBarRingoId } + returnType("Landroid/view/View;") + parameterTypes("L", "L") + instructions( + 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..9ac4138abd 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 @@ -1,16 +1,15 @@ 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.extensions.addInstruction +import app.revanced.patcher.extensions.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,103 +19,85 @@ 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( +@Suppress("unused") +val wideSearchBarPatch = bytecodePatch( name = "Wide search bar", description = "Adds an option to replace the search icon with a wide search bar. " + - "This will hide the YouTube logo when active.", + "This will hide the YouTube logo when active.", ) { dependsOn( 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 { + apply { + 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@apply Logger.getLogger(this::class.java.name).warning( + "Wide searchbar is not compatible with 20.31+", + ) + } + addResources("youtube", "layout.searchbar.wideSearchbarPatch") PreferenceScreen.FEED.addPreferences( SwitchPreference("revanced_wide_searchbar"), ) - setWordmarkHeaderFingerprint.let { - // Navigate to the method that checks if the YT logo is shown beside the search bar. - val shouldShowLogoMethod = with(it.originalMethod) { - val invokeStaticIndex = indexOfFirstInstructionOrThrow { - opcode == Opcode.INVOKE_STATIC && - getReference()?.returnType == "Z" - } - navigate(this).to(invokeStaticIndex).stop() + // Navigate to the method that checks if the YT logo is shown beside the search bar. + val shouldShowLogoMethod = with(setWordmarkHeaderMethod) { + val invokeStaticIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_STATIC && + getReference()?.returnType == "Z" } + navigate(this).to(invokeStaticIndex).stop() + } - shouldShowLogoMethod.apply { - findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index -> - val register = getInstruction(index).registerA + shouldShowLogoMethod.apply { + findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index -> + val register = getInstruction(index).registerA - addInstructionsAtControlFlowLabel( - index, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z - move-result v$register - """ - ) - } + addInstructionsAtControlFlowLabel( + index, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z + move-result v$register + """, + ) } } // Fix missing left padding when using wide searchbar. - wideSearchbarLayoutFingerprint.method.apply { + wideSearchbarLayoutMethod.apply { findInstructionIndicesReversedOrThrow { val reference = getReference() - reference?.definingClass == "Landroid/view/LayoutInflater;" - && reference.name == "inflate" + reference?.definingClass == "Landroid/view/LayoutInflater;" && + reference.name == "inflate" }.forEach { inflateIndex -> val register = getInstruction(inflateIndex + 1).registerA addInstruction( inflateIndex + 2, - "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setActionBar(Landroid/view/View;)V" + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setActionBar(Landroid/view/View;)V", ) } } 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..63cb2e0f1f 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,160 +1,147 @@ package app.revanced.patches.youtube.layout.seekbar -import app.revanced.patcher.fingerprint -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 app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val fullscreenSeekbarThumbnailsFingerprint = fingerprint { - returns("Z") +internal val BytecodePatchContext.fullscreenSeekbarThumbnailsMethod by gettingFirstMethodDeclaratively { + returnType("Z") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters() - literal { 45398577 } + parameterTypes() + instructions( + 45398577L(), + ) } -internal val playerSeekbarColorFingerprint = fingerprint { +internal val BytecodePatchContext.playerSeekbarColorMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - custom { method, _ -> - method.containsLiteralInstruction(inlineTimeBarColorizedBarPlayedColorDarkId) && - method.containsLiteralInstruction(inlineTimeBarPlayedNotHighlightedColorId) - } + instructions( + ResourceType.COLOR("inline_time_bar_played_not_highlighted_color"), + ResourceType.COLOR("inline_time_bar_colorized_bar_played_color_dark"), + ) } -internal val setSeekbarClickedColorFingerprint = fingerprint { +// class is ControlsOverlayStyle in 20.32 and lower, and obfuscated in 20.33+ +internal val BytecodePatchContext.setSeekbarClickedColorMethodMatch by composingFirstMethod( + "YOUTUBE", + "PREROLL", + "POSTROLL", + "REMOTE_LIVE", + "AD_LARGE_CONTROLS", +) { opcodes(Opcode.CONST_HIGH16) - strings("YOUTUBE", "PREROLL", "POSTROLL") - custom { _, classDef -> - classDef.endsWith("ControlsOverlayStyle;") - } } -internal val shortsSeekbarColorFingerprint = fingerprint { +internal val BytecodePatchContext.shortsSeekbarColorMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - literal { reelTimeBarPlayedColorId } + instructions( + ResourceType.COLOR("reel_time_bar_played_color"), + ) } -internal val playerSeekbarHandle1ColorFingerprint = fingerprint { +internal val BytecodePatchContext.playerSeekbarHandle1ColorMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("Landroid/content/Context;") - custom { method, _ -> - method.containsLiteralInstruction(ytTextSecondaryId) && - method.containsLiteralInstruction(ytStaticBrandRedId) - } + instructions( + ResourceType.COLOR("inline_time_bar_live_seekable_range"), + ResourceType.ATTR("ytStaticBrandRed"), + ) } -internal val playerSeekbarHandle2ColorFingerprint = fingerprint { +internal val BytecodePatchContext.playerSeekbarHandle2ColorMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("Landroid/content/Context;") - custom { method, _ -> - method.containsLiteralInstruction(inlineTimeBarLiveSeekableRangeId) && - method.containsLiteralInstruction(ytStaticBrandRedId) - } + parameterTypes("Landroid/content/Context;") + instructions( + ResourceType.ATTR("ytTextSecondary"), + ResourceType.ATTR("ytStaticBrandRed"), + ) } - -internal val watchHistoryMenuUseProgressDrawableFingerprint = fingerprint { +internal val BytecodePatchContext.watchHistoryMenuUseProgressDrawableMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - literal { -1712394514 } + returnType("V") + parameterTypes("L") + instructions( + method { name == "setMax" && definingClass == "Landroid/widget/ProgressBar;" }, + Opcode.MOVE_RESULT(), + (-1712394514L)(), + ) } -internal val lithoLinearGradientFingerprint = fingerprint { +internal val BytecodePatchContext.lithoLinearGradientMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.STATIC) - returns("Landroid/graphics/LinearGradient;") - parameters("F", "F", "F", "F", "[I", "[F") + returnType("Landroid/graphics/LinearGradient;") + parameterTypes("F", "F", "F", "F", "[I", "[F") } /** * 19.49+ */ -internal val playerLinearGradientFingerprint = fingerprint { +internal val BytecodePatchContext.playerLinearGradientMethodMatch by composingFirstMethod { 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 + parameterTypes("I", "I", "I", "I", "Landroid/content/Context;", "I") + returnType("Landroid/graphics/LinearGradient;") + instructions( + ResourceType.COLOR("yt_youtube_magenta"), + + afterAtMost(5, Opcode.FILLED_NEW_ARRAY()), + after(Opcode.MOVE_RESULT_OBJECT()), ) - literal { ytYoutubeMagentaColorId } } /** * 19.25 - 19.47 */ -internal val playerLinearGradientLegacyFingerprint = fingerprint { - returns("V") - opcodes( - Opcode.FILLED_NEW_ARRAY, - Opcode.MOVE_RESULT_OBJECT +internal val BytecodePatchContext.playerLinearGradientLegacyMethodMatch by composingFirstMethod { + returnType("V") + instructions( + ResourceType.COLOR("yt_youtube_magenta"), + + Opcode.FILLED_NEW_ARRAY(), + after(Opcode.MOVE_RESULT_OBJECT()), ) - literal { ytYoutubeMagentaColorId } -} - -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) - } } internal const val LOTTIE_ANIMATION_VIEW_CLASS_TYPE = "Lcom/airbnb/lottie/LottieAnimationView;" -internal val lottieAnimationViewSetAnimationIntFingerprint = fingerprint { +internal val BytecodePatchContext.lottieAnimationViewSetAnimationIntMethod by gettingFirstImmutableMethodDeclaratively { + definingClass(LOTTIE_ANIMATION_VIEW_CLASS_TYPE) 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 + parameterTypes("I") + returnType("V") + + lateinit var methodDefiningClass: String + custom { + methodDefiningClass = definingClass + true } + + instructions(method { name == "isInEditMode" && definingClass == methodDefiningClass }) } -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) - } -} - -internal val lottieCompositionFactoryZipFingerprint = fingerprint { +internal val BytecodePatchContext.lottieCompositionFactoryZipMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - parameters("Landroid/content/Context;", "Ljava/lang/String;", "Ljava/lang/String;") - returns("L") - strings(".zip", ".lottie") + parameterTypes("Landroid/content/Context;", "Ljava/util/zip/ZipInputStream;", "Ljava/lang/String;") + returnType("L") + instructions( + "Unable to parse composition"(), + " however it was not found in the animation."(), + ) } /** - * Resolves using class found in [lottieCompositionFactoryZipFingerprint]. + * Resolves using class found in [lottieCompositionFactoryZipMethod]. * * [Original method](https://github.com/airbnb/lottie-android/blob/26ad8bab274eac3f93dccccfa0cafc39f7408d13/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java#L386) */ -internal val lottieCompositionFactoryFromJsonInputStreamFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getLottieCompositionFactoryFromJsonInputStreamMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - parameters("Ljava/io/InputStream;", "Ljava/lang/String;") - returns("L") - literal { 2 } + parameterTypes("Ljava/io/InputStream;", "Ljava/lang/String;") + returnType("L") + instructions( + anyOf(2L(), 3L()), + ) } - - 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..9a67e0491c 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 @@ -1,30 +1,25 @@ package app.revanced.patches.youtube.layout.seekbar -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.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.* +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction 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_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.patches.youtube.shared.mainActivityOnCreateMethod 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 +28,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 +36,31 @@ val seekbarColorPatch = bytecodePatch( dependsOn( sharedExtensionPatch, lithoColorHookPatch, - seekbarColorResourcePatch, - versionCheckPatch + resourceMappingPatch, + versionCheckPatch, ) - execute { - fun MutableMethod.addColorChangeInstructions(resourceId: Long) { + apply { + fun MutableMethod.addColorChangeInstructions(index: Int) { insertLiteralOverride( - resourceId, - "$EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I" + index, + "$EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I", ) } - playerSeekbarColorFingerprint.method.apply { - addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDarkId) - addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColorId) + playerSeekbarColorMethodMatch.let { + it.method.apply { + addColorChangeInstructions(it[-1]) + addColorChangeInstructions(it[0]) + } } - shortsSeekbarColorFingerprint.method.apply { - addColorChangeInstructions(reelTimeBarPlayedColorId) + shortsSeekbarColorMethodMatch.let { + it.method.addColorChangeInstructions(it[0]) } - setSeekbarClickedColorFingerprint.originalMethod.let { - val setColorMethodIndex = setSeekbarClickedColorFingerprint.patternMatch!!.startIndex + 1 + setSeekbarClickedColorMethodMatch.immutableMethod.let { + val setColorMethodIndex = setSeekbarClickedColorMethodMatch[0] + 1 navigate(it).to(setColorMethodIndex).stop().apply { val colorRegister = getInstruction(0).registerA @@ -140,56 +78,55 @@ val seekbarColorPatch = bytecodePatch( // 19.25+ changes - arrayOf( - playerSeekbarHandle1ColorFingerprint, - playerSeekbarHandle2ColorFingerprint - ).forEach { - it.method.addColorChangeInstructions(ytStaticBrandRedId) + var handleBarColorMethodMatches = mutableListOf(playerSeekbarHandle1ColorMethodMatch) + if (!is_20_34_or_greater) { + handleBarColorMethodMatches += playerSeekbarHandle2ColorMethodMatch + } + handleBarColorMethodMatches.forEach { + it.method.addColorChangeInstructions(it[-1]) } // 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 + watchHistoryMenuUseProgressDrawableMethodMatch.let { + it.method.apply { + val index = it[1] + 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 + """, + ) + } } } - lithoLinearGradientFingerprint.method.addInstructions( + lithoLinearGradientMethod.addInstructions( 0, """ invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->getLithoLinearGradient([I[F)[I move-result-object p4 - """ + """, ) - val playerFingerprint: Fingerprint + val playerMatch: CompositeMatch val checkGradientCoordinates: Boolean if (is_19_49_or_greater) { - playerFingerprint = playerLinearGradientFingerprint + playerMatch = playerLinearGradientMethodMatch checkGradientCoordinates = true } else { - playerFingerprint = playerLinearGradientLegacyFingerprint + playerMatch = playerLinearGradientLegacyMethodMatch checkGradientCoordinates = false } - playerFingerprint.let { + playerMatch.let { it.method.apply { - val index = it.patternMatch!!.endIndex + val index = it[-1] val register = getInstruction(index).registerA addInstructions( @@ -204,7 +141,7 @@ val seekbarColorPatch = bytecodePatch( invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getPlayerLinearGradient([I)[I move-result-object v$register """ - } + }, ) } } @@ -212,99 +149,97 @@ val seekbarColorPatch = bytecodePatch( // region apply seekbar custom color to splash screen animation. if (!is_19_34_or_greater) { - 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" - ) + return@apply // 19.25 does not have a cairo launch animation. } // Hook the splash animation to set the a seekbar color. - mainActivityOnCreateFingerprint.method.apply { - val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name + mainActivityOnCreateMethod.apply { + val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntMethod.name findInstructionIndicesReversedOrThrow { val reference = getReference() - reference?.definingClass == "Lcom/airbnb/lottie/LottieAnimationView;" - && reference.name == setAnimationIntMethodName + reference?.definingClass == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && + reference.name == setAnimationIntMethodName }.forEach { index -> val instruction = getInstruction(index) 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", ) } } // Add non obfuscated method aliases for `setAnimation(int)` // and `setAnimation(InputStream, String)` so extension code can call them. - lottieAnimationViewSetAnimationIntFingerprint.classDef.methods.apply { + lottieAnimationViewSetAnimationIntMethod.classDef.methods.apply { val addedMethodName = "patch_setAnimation" - val setAnimationIntName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name + val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntMethod.name - add(ImmutableMethod( - LOTTIE_ANIMATION_VIEW_CLASS_TYPE, - addedMethodName, - listOf(ImmutableMethodParameter("I", null, null)), - "V", - AccessFlags.PUBLIC.value, - null, - null, - MutableMethodImplementation(2), - ).toMutable().apply { - addInstructions( - """ - invoke-virtual { p0, p1 }, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationIntName(I)V - return-void - """ - ) - }) + add( + ImmutableMethod( + LOTTIE_ANIMATION_VIEW_CLASS_TYPE, + addedMethodName, + listOf(ImmutableMethodParameter("I", null, null)), + "V", + AccessFlags.PUBLIC.value, + null, + null, + MutableMethodImplementation(2), + ).toMutable().apply { + addInstructions( + """ + invoke-virtual { p0, p1 }, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationIntMethodName(I)V + return-void + """, + ) + }, + ) - val factoryStreamClass : CharSequence - val factoryStreamName : CharSequence - val factoryStreamReturnType : CharSequence - lottieCompositionFactoryFromJsonInputStreamFingerprint.match( - lottieCompositionFactoryZipFingerprint.originalClassDef - ).originalMethod.apply { - factoryStreamClass = definingClass - factoryStreamName = name - factoryStreamReturnType = returnType + val factoryStreamClass: CharSequence + val factoryStreamName: CharSequence + val factoryStreamReturnType: CharSequence + + lottieCompositionFactoryZipMethod.immutableClassDef.getLottieCompositionFactoryFromJsonInputStreamMethod() + .let { + factoryStreamClass = it.definingClass + factoryStreamName = it.name + factoryStreamReturnType = it.returnType + } + + val lottieAnimationViewSetAnimationStreamMethod = firstMethodDeclaratively { + definingClass(lottieAnimationViewSetAnimationIntMethod.immutableClassDef.type) + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameterTypes(factoryStreamReturnType.toString()) + returnType("V") } + val setAnimationStreamMethodName = lottieAnimationViewSetAnimationStreamMethod.name - val setAnimationStreamName = lottieAnimationViewSetAnimationStreamFingerprint - .originalMethod.name - - add(ImmutableMethod( - LOTTIE_ANIMATION_VIEW_CLASS_TYPE, - addedMethodName, - listOf( - ImmutableMethodParameter("Ljava/io/InputStream;", null, null), - ImmutableMethodParameter("Ljava/lang/String;", null, null) - ), - "V", - AccessFlags.PUBLIC.value, - null, - null, - MutableMethodImplementation(4), - ).toMutable().apply { - addInstructions( - """ - invoke-static { p1, p2 }, $factoryStreamClass->$factoryStreamName(Ljava/io/InputStream;Ljava/lang/String;)$factoryStreamReturnType - move-result-object v0 - invoke-virtual { p0, v0}, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationStreamName($factoryStreamReturnType)V - return-void - """ - ) - }) + add( + ImmutableMethod( + LOTTIE_ANIMATION_VIEW_CLASS_TYPE, + addedMethodName, + listOf( + ImmutableMethodParameter("Ljava/io/InputStream;", null, null), + ImmutableMethodParameter("Ljava/lang/String;", null, null), + ), + "V", + AccessFlags.PUBLIC.value, + null, + null, + MutableMethodImplementation(4), + ).toMutable().apply { + addInstructions( + """ + invoke-static { p1, p2 }, $factoryStreamClass->$factoryStreamName(Ljava/io/InputStream;Ljava/lang/String;)$factoryStreamReturnType + move-result-object v0 + invoke-virtual { p0, v0}, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationStreamMethodName($factoryStreamReturnType)V + return-void + """, + ) + }, + ) } // endregion 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..f6b30c4650 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,61 @@ package app.revanced.patches.youtube.layout.shortsautoplay -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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 +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val reelEnumConstructorFingerprint = fingerprint { +internal val BytecodePatchContext.reelEnumConstructorMethodMatch by composingFirstMethod { 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( + "REEL_LOOP_BEHAVIOR_UNKNOWN"(), + "REEL_LOOP_BEHAVIOR_SINGLE_PLAY"(), + "REEL_LOOP_BEHAVIOR_REPEAT"(), + "REEL_LOOP_BEHAVIOR_END_SCREEN"(), + Opcode.RETURN_VOID(), ) } -internal val reelPlaybackRepeatFingerprint = fingerprint { - returns("V") - parameters("L") - strings("YoutubePlayerState is in throwing an Error.") +internal val BytecodePatchContext.reelPlaybackRepeatParentMethod by gettingFirstImmutableMethodDeclaratively { + returnType("V") + parameterTypes("Ljava/lang/String;", "J") + instructions( + "Reels[%s] Playback Time: %d ms"(), + ) } -internal val reelPlaybackFingerprint = fingerprint { +/** + * Matches class found in [reelPlaybackRepeatParentMethod]. + */ +context(_: BytecodePatchContext) +internal fun ClassDef.getReelPlaybackRepeatMethod() = firstMethodDeclaratively { + returnType("V") + parameterTypes("L") + instructions(method { toString() == "Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z" }) +} + +internal val BytecodePatchContext.reelPlaybackMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("J") - custom { method, _ -> - indexOfMilliSecondsInstruction(method) >= 0 && - indexOfInitializationInstruction(method) >= 0 - } + parameterTypes("J") + returnType("V") + + val methodParametersPrefix = listOf("I", "L", "L") + instructions( + field { definingClass == "Ljava/util/concurrent/TimeUnit;" && name == "MILLISECONDS" }, + afterAtMost( + 15, + method { + name == "" && + parameterTypes.zip(methodParametersPrefix).all { (a, b) -> a.startsWith(b) } + }, + ), + afterAtMost( + 5, + allOf( + Opcode.INVOKE_VIRTUAL(), + method { returnType == "I" && parameterTypes.count() == 1 && parameterTypes.first().startsWith("L") }, + ), + ), + ) } - -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" - } 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..988529db90 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 @@ -1,11 +1,14 @@ package app.revanced.patches.youtube.layout.shortsautoplay -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.methodReference +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference @@ -15,7 +18,7 @@ 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 app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow @@ -31,6 +34,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;" +@Suppress("ObjectPropertyName") val shortsAutoplayPatch = bytecodePatch( name = "Shorts autoplay", description = "Adds options to automatically play the next Short.", @@ -44,14 +48,14 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.shortsautoplay.shortsAutoplayPatch") PreferenceScreen.SHORTS.addPreferences( @@ -65,18 +69,18 @@ val shortsAutoplayPatch = bytecodePatch( } // Main activity is used to check if app is in pip mode. - mainActivityOnCreateFingerprint.method.addInstruction( + mainActivityOnCreateMethod.addInstruction( 0, "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 + reelEnumConstructorMethodMatch.apply { + reelEnumClass = immutableClassDef.type - addInstructions( - insertIndex, + method.addInstructions( + reelEnumConstructorMethodMatch[-1], """ # Pass the first enum value to extension. # Any enum value of this type will work. @@ -86,7 +90,9 @@ val shortsAutoplayPatch = bytecodePatch( ) } - reelPlaybackRepeatFingerprint.method.apply { + val reelPlaybackRepeatMethod = reelPlaybackRepeatParentMethod.immutableClassDef.getReelPlaybackRepeatMethod() + + reelPlaybackRepeatMethod.apply { // The behavior enums are looked up from an ordinal value to an enum type. findInstructionIndicesReversedOrThrow { val reference = getReference() @@ -110,26 +116,23 @@ 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 = + reelPlaybackMethodMatch.method.getInstruction(reelPlaybackMethodMatch[1]).methodReference!! + val reelSequenceControllerMethodReference = + reelPlaybackMethodMatch.method.getInstruction(reelPlaybackMethodMatch[2]).methodReference!! - reelPlaybackRepeatFingerprint.method.apply { + reelPlaybackRepeatMethod.apply { // Find the first call modified by extension code above. val extensionReturnResultIndex = indexOfFirstInstructionOrThrow { opcode == Opcode.INVOKE_STATIC && - getReference()?.definingClass == EXTENSION_CLASS_DESCRIPTOR + 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 && - reference.type == reelSequenceControllerMethodReference.definingClass + reference?.definingClass == definingClass && + reference.type == reelSequenceControllerMethodReference.definingClass } val getReelSequenceControllerReference = getInstruction(getReelSequenceControllerIndex).reference @@ -166,10 +169,10 @@ val shortsAutoplayPatch = bytecodePatch( return-object v4 :ignore return-object p1 - """ + """, ) } - reelPlaybackRepeatFingerprint.classDef.methods.add(helperMethod) + reelPlaybackRepeatMethod.classDef.methods.add(helperMethod) addInstructionsWithLabels( extensionReturnResultIndex + 1, @@ -180,7 +183,7 @@ val shortsAutoplayPatch = bytecodePatch( return-void # Autoplay was performed. :ignore nop - """ + """, ) } } 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..7fb5ae1ca0 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,64 +1,97 @@ package app.revanced.patches.youtube.layout.shortsplayer -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType 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;", +internal val BytecodePatchContext.playbackStartFeatureFlagMethodMatch by composingFirstMethod { + returnType("Z") + parameterTypes("Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;") + instructions( + method { + definingClass == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;" && + returnType == "Ljava/lang/String;" + }, + 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 BytecodePatchContext.watchPanelVideoIdMethodMatch by composingFirstMethod { + returnType("Ljava/lang/String;") + parameterTypes() + instructions( + allOf( + Opcode.IGET_OBJECT(), + field { type == "Lcom/google/android/apps/youtube/app/common/player/queue/WatchPanelId;" }, + ), + allOf( + Opcode.CHECK_CAST(), + type("Lcom/google/android/apps/youtube/app/common/player/queue/DefaultWatchPanelId;"), + ), + method { + definingClass == "Lcom/google/android/apps/youtube/app/common/player/queue/DefaultWatchPanelId;" && + returnType == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;" + }, + method { + definingClass == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;" && + returnType == "Ljava/lang/String;" + }, + ) - literal { - 45380134L - } } // Pre 19.25 -internal val shortsPlaybackIntentLegacyFingerprint = fingerprint { +internal val BytecodePatchContext.shortsPlaybackIntentLegacyMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters( + returnType("V") + parameterTypes( "L", "Ljava/util/Map;", "J", "Ljava/lang/String;", "Z", - "Ljava/util/Map;" + "Ljava/util/Map;", ) - strings( + instructions( + method { 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" + "com.google.android.apps.youtube.app.endpoint.flags"(), + "ReelWatchFragmentArgs"(), + "reels_fragment_descriptor"(), ) } -internal val shortsPlaybackIntentFingerprint = fingerprint { +internal val BytecodePatchContext.shortsPlaybackIntentMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) - returns("V") - parameters( + returnType("V") + parameterTypes( "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;", "Ljava/util/Map;", "J", - "Ljava/lang/String;" + "Ljava/lang/String;", ) - strings( + instructions( // None of these strings are unique. - "com.google.android.apps.youtube.app.endpoint.flags", - "ReelWatchFragmentArgs", - "reels_fragment_descriptor" + "com.google.android.apps.youtube.app.endpoint.flags"(), + "ReelWatchFragmentArgs"(), + "reels_fragment_descriptor"(), ) } -internal val exitVideoPlayerFingerprint = fingerprint { - returns("V") - parameters() - literal { - mdx_drawer_layout_id - } -} \ No newline at end of file +internal val BytecodePatchContext.exitVideoPlayerMethod by gettingFirstMethodDeclaratively { + returnType("V") + parameterTypes() + instructions(ResourceType.ID("mdx_drawer_layout")) +} 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..9b115730b4 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 @@ -1,26 +1,23 @@ package app.revanced.patches.youtube.layout.shortsplayer -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.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 -import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod import app.revanced.util.findFreeRegister import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow @@ -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,50 +41,43 @@ 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", + ), ) - execute { + apply { 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. - mainActivityOnCreateFingerprint.method.addInstruction( + mainActivityOnCreateMethod.addInstruction( 0, "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" + - "setMainActivity(Landroid/app/Activity;)V", + "setMainActivity(Landroid/app/Activity;)V", ) // 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) { + watchPanelVideoIdMethodMatch.let { + it.immutableMethod to it[-1] + } + } else { + playbackStartFeatureFlagMethodMatch.let { + it.immutableMethod to it[0] } - - navigate(it).to(stringMethodIndex).stop().name } + val playbackStartVideoIdMethodName = navigate(videoIdStartMethod).to(videoIdIndex).stop().name fun extensionInstructions(playbackStartRegister: Int, freeRegister: Int) = """ @@ -117,37 +92,34 @@ 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) { + shortsPlaybackIntentMethod.addInstructionsWithLabels( + 0, + """ + move-object/from16 v0, p1 + ${extensionInstructions(0, 1)} + """, + ) + } else { + shortsPlaybackIntentLegacyMethodMatch.let { + it.method.apply { + val index = it[0] + 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. - exitVideoPlayerFingerprint.method.apply { + exitVideoPlayerMethod.apply { // Method call for Activity.finish() val finishIndex = indexOfFirstInstructionOrThrow { val reference = getReference() @@ -163,7 +135,7 @@ val openShortsInRegularPlayerPatch = bytecodePatch( """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->overrideBackPressToExit(Z)Z move-result v$register - """ + """, ) } } 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..de6ee8f0e4 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,78 +1,50 @@ package app.revanced.patches.youtube.layout.sponsorblock -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionReversed +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType +import app.revanced.patches.youtube.shared.seekbarMethod 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 +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val appendTimeFingerprint = fingerprint { - returns("V") +internal val BytecodePatchContext.appendTimeMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - 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, + returnType("V") + parameterTypes("Ljava/lang/CharSequence;", "Ljava/lang/CharSequence;", "Ljava/lang/CharSequence;") + instructions( + ResourceType.STRING("total_time"), + method { toString() == "Landroid/content/res/Resources;->getString(I[Ljava/lang/Object;)Ljava/lang/String;" }, + after(Opcode.MOVE_RESULT_OBJECT()), ) } -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, +internal val ClassDef.controlsOverlayMethodMatch by ClassDefComposing.composingFirstMethod { + returnType("V") + parameterTypes() + instructions( + ResourceType.ID.invoke("inset_overlay_view_layout"), + afterAtMost(20, allOf(Opcode.CHECK_CAST(), type("Landroid/widget/FrameLayout;"))), ) } -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 - } +/** + * Resolves to the class found in [seekbarMethod]. + */ +internal val ClassDef.rectangleFieldInvalidatorMethodMatch by ClassDefComposing.composingFirstMethod { + returnType("V") + parameterTypes() + instructions(method("invalidate")) } -internal val adProgressTextViewVisibilityFingerprint = fingerprint { +internal val BytecodePatchContext.adProgressTextViewVisibilityMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Z") - custom { method, _ -> - indexOfAdProgressTextViewVisibilityInstruction(method) >= 0 - } -} - -internal fun indexOfAdProgressTextViewVisibilityInstruction(method: Method) = - method.indexOfFirstInstructionReversed { - val reference = getReference() - reference?.definingClass == + returnType("V") + parameterTypes("Z") + instructions( + method { + name == "setVisibility" && definingClass == "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..d17441dea9 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 @@ -1,14 +1,11 @@ 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.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef 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 +13,27 @@ 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.getLayoutConstructorMethodMatch +import app.revanced.patches.youtube.shared.seekbarMethod +import app.revanced.patches.youtube.shared.getSeekbarOnDrawMethodMatch 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 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( @@ -41,7 +43,7 @@ private val sponsorBlockResourcePatch = resourcePatch { playerControlsPatch, ) - execute { + apply { addResources("youtube", "layout.sponsorblock.sponsorBlockResourcePatch") PreferenceScreen.SPONSORBLOCK.addPreferences( @@ -51,13 +53,13 @@ private val sponsorBlockResourcePatch = resourcePatch { key = "revanced_settings_screen_10_sponsorblock", sorting = PreferenceScreenPreference.Sorting.UNSORTED, preferences = emptySet(), // Preferences are added by custom class at runtime. - tag = "app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockPreferenceGroup" + tag = "app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockPreferenceGroup", ), PreferenceCategory( key = "revanced_sb_stats", sorting = PreferenceScreenPreference.Sorting.UNSORTED, preferences = emptySet(), // Preferences are added by custom class at runtime. - tag = "app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockStatsPreferenceCategory" + tag = "app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockStatsPreferenceCategory", ), PreferenceCategory( key = "revanced_sb_about", @@ -67,9 +69,9 @@ private val sponsorBlockResourcePatch = resourcePatch { key = "revanced_sb_about_api", tag = "app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockAboutPreference", selectable = true, - ) - ) - ) + ), + ), + ), ) arrayOf( @@ -80,7 +82,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,9 +89,10 @@ 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", - ) + ), ).forEach { resourceGroup -> copyResources("sponsorblock", resourceGroup) } @@ -99,7 +101,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 +117,7 @@ val sponsorBlockPatch = bytecodePatch( ) { dependsOn( sharedExtensionPatch, + resourceMappingPatch, videoIdPatch, // Required to skip segments on time. videoInformationPatch, @@ -126,14 +129,14 @@ 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", + ), ) - execute { + apply { // Hook the video time methods. videoTimeHook( EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR, @@ -145,42 +148,59 @@ 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) - - addInstruction( - moveRectangleToRegisterIndex + 1, - "invoke-static/range { p0 .. p0 }, " + - "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;)V", - ) - - // Set the thickness of the segment. - val thicknessIndex = indexOfFirstInstructionOrThrow { - opcode == Opcode.INVOKE_STATIC && getReference()?.name == "round" + // Set seekbar draw rectangle. + val rectangleFieldName: FieldReference + seekbarMethod.immutableClassDef.rectangleFieldInvalidatorMethodMatch.let { + it.method.apply { + val rectangleIndex = indexOfFirstInstructionReversedOrThrow( + it[0], + ) { + getReference()?.type == "Landroid/graphics/Rect;" + } + rectangleFieldName = getInstruction(rectangleIndex).reference as FieldReference } - val thicknessRegister = getInstruction(thicknessIndex).registerC - addInstruction( - thicknessIndex + 2, - "invoke-static { v$thicknessRegister }, " + - "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarThickness(I)V", - ) + } - // Find the drawCircle call and draw the segment before it. - val drawCircleIndex = indexOfFirstInstructionReversedOrThrow { - getReference()?.name == "drawCircle" + // Seekbar drawing. + + // Cannot match using original immutable class because + // class may have been modified by other patches + seekbarMethod.immutableClassDef.getSeekbarOnDrawMethodMatch().let { + it.method.apply { + // Set seekbar thickness. + val thicknessIndex = it[-1] + 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 + + 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 + """, + ) } - 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", - ) } // Change visibility of the buttons. @@ -191,75 +211,45 @@ 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 + appendTimeMethodMatch.let { + it.method.apply { + val index = it[-1] + 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. onCreateHook(EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR, "initialize") // Initialize the SponsorBlock view. - controlsOverlayFingerprint.match(layoutConstructorFingerprint.originalClassDef).let { - val startIndex = it.patternMatch!!.startIndex + getLayoutConstructorMethodMatch().immutableClassDef.controlsOverlayMethodMatch.let { + val checkCastIndex = it[-1] + 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 + adProgressTextViewVisibilityMethodMatch.let { + val setVisibilityIndex = it[0] + val register = it.method.getInstruction(setVisibilityIndex).registerD - // 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 - - addInstructionsAtControlFlowLabel( - index, - "invoke-static { v$register }, $EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setAdProgressTextVisibility(I)V" + it.method.addInstructionsAtControlFlowLabel( + setVisibilityIndex, + "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..2d316f7623 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,41 +1,38 @@ package app.revanced.patches.youtube.layout.spoofappversion -import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType 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") +internal val BytecodePatchContext.toolBarButtonMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Landroid/view/MenuItem;") - custom { method, _ -> - method.containsLiteralInstruction(menuItemView) && - indexOfGetDrawableInstruction(method) >= 0 - } + returnType("V") + instructions( + ResourceType.ID("menu_item_view"), + allOf(Opcode.INVOKE_INTERFACE(), method { returnType == "I" }), + after(Opcode.MOVE_RESULT()), + afterAtMost(6, allOf(Opcode.IGET_OBJECT(), field { type == "Landroid/widget/ImageView;" })), + afterAtMost(8, method { name == "getDrawable" && definingClass == "Landroid/content/res/Resources;" }), + afterAtMost(4, method { name == "setImageDrawable" && definingClass == "Landroid/widget/ImageView;" }), + ) + // 20.37+ has second parameter of "Landroid/content/Context;" + custom { parameterTypes.count() in 1..2 && parameterTypes.first() == "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 { +internal val BytecodePatchContext.spoofAppVersionMethodMatch by composingFirstMethod( + // Instead of applying a bytecode patch, it might be possible to only rely on code from the extension and + // manually set the desired version string as this keyed value in the SharedPreferences. + // But, this bytecode patch is simple and it works. + "pref_override_build_version_name", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("L") - parameters("L") + returnType("L") + parameterTypes("L") opcodes( Opcode.IGET_OBJECT, Opcode.GOTO, Opcode.CONST_STRING, ) - // Instead of applying a bytecode patch, it might be possible to only rely on code from the extension and - // manually set the desired version string as this keyed value in the SharedPreferences. - // But, this bytecode patch is simple and it works. - strings("pref_override_build_version_name") } 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..cb9259325f 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 @@ -1,16 +1,13 @@ package app.revanced.patches.youtube.layout.spoofappversion -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.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.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,52 +18,35 @@ 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;" +@Suppress("ObjectPropertyName") val spoofAppVersionPatch = bytecodePatch( name = "Spoof app version", description = "Adds an option to trick YouTube into thinking you are running an older version of the app. " + - "This can be used to restore old UI elements and features." + "This can be used to restore old UI elements and features.", ) { dependsOn( - spoofAppVersionResourcePatch, + resourceMappingPatch, sharedExtensionPatch, settingsPatch, addResourcesPatch, - versionCheckPatch + 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", + ), ) - execute { + apply { addResources("youtube", "layout.spoofappversion.spoofAppVersionPatch") PreferenceScreen.GENERAL_LAYOUT.addPreferences( @@ -83,20 +63,18 @@ 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" + 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" + entryValuesKey = "revanced_spoof_app_version_target_legacy_19_34_entry_values", ) - } - ) - ) + }, + ), + ), ) /** @@ -104,36 +82,28 @@ 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 + toolBarButtonMethodMatch.let { + val imageResourceIndex = it[2] + val register = it.method.getInstruction(imageResourceIndex).registerA + val jumpIndex = it[-1] + 1 - addInstructionsWithLabels( - insertIndex, - "if-eqz v$insertRegister, :ignore", - ExternalLabel("ignore", getInstruction(jumpIndex)) + it.method.addInstructionsWithLabels( + imageResourceIndex + 1, + "if-eqz v$register, :ignore", + ExternalLabel("ignore", it.method.getInstruction(jumpIndex)), ) } - spoofAppVersionFingerprint.apply { - val startIndex = patternMatch!!.startIndex - val buildOverrideNameRegister = method.getInstruction(startIndex).registerA + spoofAppVersionMethodMatch.let { + val index = it[0] + val register = it.method.getInstruction(index).registerA - method.addInstructions( - startIndex + 1, - """ - invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$buildOverrideNameRegister + it.method.addInstructions( + index + 1, """ + 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..ec86e1d561 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 @@ -1,8 +1,8 @@ package app.revanced.patches.youtube.layout.startpage -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.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -13,13 +13,11 @@ 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;" +@Suppress("unused") val changeStartPagePatch = bytecodePatch( name = "Change start page", description = "Adds an option to set which page the app opens in instead of the homepage.", @@ -32,14 +30,14 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.startpage.changeStartPagePatch") PreferenceScreen.GENERAL_LAYOUT.addPreferences( @@ -50,32 +48,32 @@ val changeStartPagePatch = bytecodePatch( preferences = setOf( ListPreference( key = "revanced_change_start_page", - tag = "app.revanced.extension.shared.settings.preference.SortedListPreference" + tag = "app.revanced.extension.shared.settings.preference.SortedListPreference", ), - SwitchPreference("revanced_change_start_page_always") - ) - ) + SwitchPreference("revanced_change_start_page_always"), + ), + ), ) // Hook browseId. - browseIdFingerprint.method.apply { - val browseIdIndex = indexOfFirstInstructionOrThrow { - getReference()?.string == "FEwhat_to_watch" - } - val browseIdRegister = getInstruction(browseIdIndex).registerA + browseIdMethodMatch.let { + it.method.apply { + val browseIdIndex = it[0] + 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. // Just hook the Intent action. - intentActionFingerprint.method.addInstruction( + intentActionMethod.addInstruction( 0, "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->overrideIntentAction(Landroid/content/Intent;)V", ) 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..6c172b1513 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,21 @@ package app.revanced.patches.youtube.layout.startpage -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.Opcode -internal val intentActionFingerprint = fingerprint { - parameters("Landroid/content/Intent;") - strings("has_handled_intent") +internal val BytecodePatchContext.intentActionMethod by gettingFirstMethodDeclaratively( + "has_handled_intent", +) { + parameterTypes("Landroid/content/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, +internal val BytecodePatchContext.browseIdMethodMatch by composingFirstMethod { + returnType("Lcom/google/android/apps/youtube/app/common/ui/navigation/PaneDescriptor;") + // parameterTypes() // 20.30 and earlier is no parameters. 20.31+ parameter is L. + instructions( + "FEwhat_to_watch"(), + 512L(), + allOf(Opcode.IPUT_OBJECT(), field { 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..3b8023f26c 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,20 @@ 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.extensions.addInstructions +import app.revanced.patcher.extensions.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 @@ -22,6 +22,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisableResumingStartupShortsPlayerPatch;" +@Suppress("unused") val disableResumingShortsOnStartupPatch = bytecodePatch( name = "Disable resuming Shorts on startup", description = "Adds an option to disable the Shorts player from resuming on app startup when Shorts were last being watched.", @@ -30,68 +31,64 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.startupshortsreset.disableResumingShortsOnStartupPatch") PreferenceScreen.SHORTS.addPreferences( SwitchPreference("revanced_disable_resuming_shorts_player"), ) - if (is_20_02_or_greater) { - userWasInShortsAlternativeFingerprint.let { + if (is_20_03_or_greater) { + userWasInShortsAlternativeMethodMatch.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 insertIndex = it[2] + 1 + val register = getInstruction(insertIndex).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 { + userWasInShortsLegacyMethod.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 - """ + """, ) } } - userWasInShortsConfigFingerprint.method.addInstructions( + userWasInShortsConfigMethod.addInstructions( 0, """ invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z @@ -101,7 +98,7 @@ val disableResumingShortsOnStartupPatch = bytecodePatch( return v0 :show 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..931bff6052 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,33 +1,46 @@ package app.revanced.patches.youtube.layout.startupshortsreset -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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") +internal val BytecodePatchContext.userWasInShortsAlternativeMethodMatch by composingFirstMethod { + returnType("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Ljava/lang/Object;") - strings("userIsInShorts: ") + parameterTypes("Ljava/lang/Object;") + instructions( + allOf(Opcode.CHECK_CAST(), type("Ljava/lang/Boolean;")), + after(method { toString() == "Ljava/lang/Boolean;->booleanValue()Z" }), + after(Opcode.MOVE_RESULT()), + // 20.40+ string was merged into another string and is a partial match. + afterAtMost(15, "userIsInShorts: "(String::contains)), + ) } -internal val userWasInShortsLegacyFingerprint = fingerprint { - returns("V") +/** + * Pre 20.02 + */ +internal val BytecodePatchContext.userWasInShortsLegacyMethod by gettingFirstMethodDeclaratively { + returnType("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - parameters("Ljava/lang/Object;") - strings("Failed to read user_was_in_shorts proto after successful warmup") + parameterTypes("Ljava/lang/Object;") + instructions( + "Failed to read user_was_in_shorts proto after successful warmup"(), + ) } /** * 18.15.40+ */ -internal val userWasInShortsConfigFingerprint = fingerprint { +internal val BytecodePatchContext.userWasInShortsConfigMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - literal { - 45358360L - } + returnType("Z") + parameterTypes() + instructions( + 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..c89783668c 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,22 +1,22 @@ package app.revanced.patches.youtube.layout.theme -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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 } +internal val BytecodePatchContext.useGradientLoadingScreenMethodMatch by composingFirstMethod { + instructions(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 } - custom { method, classDef -> - method.name == "onCreate" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE - } +internal val BytecodePatchContext.splashScreenStyleMethodMatch by composingFirstMethod { + definingClass(YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE) + name("onCreate") + returnType("V") + parameterTypes("Landroid/os/Bundle;") + instructions( + anyOf( + 1074339245L(), // 20.30+ + 269032877L(), // 20.29 and lower. + ), + ) } 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..dad35b727a 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 @@ -33,9 +33,9 @@ val themePatch = baseThemePatch( block = { val lightThemeBackgroundColor by stringOption( - key = "lightThemeBackgroundColor", + name = "Light theme background color", default = "@android:color/white", - values = mapOf( + values = mapOf( "White" to "@android:color/white", "Material You" to "@android:color/system_neutral1_50", "Catppuccin (Latte)" to "#E6E9EF", @@ -46,17 +46,16 @@ val themePatch = baseThemePatch( "Light orange" to "#FFE6CC", "Light red" to "#FFD6D6", ), - title = "Light theme background color", - description = THEME_COLOR_OPTION_DESCRIPTION + description = THEME_COLOR_OPTION_DESCRIPTION, ) val themeResourcePatch = resourcePatch { dependsOn(resourceMappingPatch) - execute { + apply { overrideThemeColors( lightThemeBackgroundColor!!, - darkThemeBackgroundColorOption.value!! + darkThemeBackgroundColorOption.value!!, ) fun addColorResource( @@ -73,7 +72,7 @@ val themePatch = baseThemePatch( setAttribute("name", colorName) setAttribute("category", "color") textContent = colorValue - } + }, ) } } @@ -83,12 +82,12 @@ val themePatch = baseThemePatch( addColorResource( "res/values/colors.xml", splashBackgroundColorKey, - lightThemeBackgroundColor!! + lightThemeBackgroundColor!!, ) addColorResource( "res/values-night/colors.xml", splashBackgroundColorKey, - darkThemeBackgroundColorOption.value!! + darkThemeBackgroundColorOption.value!!, ) // Edit splash screen files and change the background color. @@ -98,12 +97,12 @@ val themePatch = baseThemePatch( ).forEach editSplashScreen@{ resourceFileName -> document(resourceFileName).use { document -> document.getElementsByTagName( - "layer-list" + "layer-list", ).item(0).forEachChildElement { node -> if (node.hasAttribute("android:drawable")) { node.setAttribute( "android:drawable", - "@color/$splashBackgroundColorKey" + "@color/$splashBackgroundColorKey", ) return@editSplashScreen } @@ -115,7 +114,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") @@ -149,18 +148,18 @@ val themePatch = baseThemePatch( addResourcesPatch, seekbarColorPatch, baseThemeResourcePatch( - lightColorReplacement = { lightThemeBackgroundColor!! } + lightColorReplacement = { lightThemeBackgroundColor!! }, ), - themeResourcePatch + themeResourcePatch, ) 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", + ), ) }, @@ -168,7 +167,7 @@ val themePatch = baseThemePatch( addResources("youtube", "layout.theme.themePatch") PreferenceScreen.GENERAL_LAYOUT.addPreferences( - SwitchPreference("revanced_gradient_loading_screen") + SwitchPreference("revanced_gradient_loading_screen"), ) val preferences = mutableSetOf( @@ -176,13 +175,13 @@ val themePatch = baseThemePatch( TextPreference( "revanced_seekbar_custom_color_primary", tag = "app.revanced.extension.shared.settings.preference.ColorPickerPreference", - inputType = InputType.TEXT_CAP_CHARACTERS + inputType = InputType.TEXT_CAP_CHARACTERS, ), TextPreference( "revanced_seekbar_custom_color_accent", tag = "app.revanced.extension.shared.settings.preference.ColorPickerPreference", - inputType = InputType.TEXT_CAP_CHARACTERS - ) + inputType = InputType.TEXT_CAP_CHARACTERS, + ), ) PreferenceScreen.SEEKBAR.addPreferences( @@ -190,27 +189,27 @@ val themePatch = baseThemePatch( titleKey = null, sorting = Sorting.UNSORTED, tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory", - preferences = preferences - ) + preferences = preferences, + ), ) if (is_19_47_or_greater) { PreferenceScreen.GENERAL_LAYOUT.addPreferences( - ListPreference("revanced_splash_screen_animation_style") + ListPreference("revanced_splash_screen_animation_style"), ) } - useGradientLoadingScreenFingerprint.method.insertLiteralOverride( - GRADIENT_LOADING_SCREEN_AB_CONSTANT, - "$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z" + useGradientLoadingScreenMethodMatch.method.insertLiteralOverride( + useGradientLoadingScreenMethodMatch[0], + "$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" + splashScreenStyleMethodMatch.method.insertLiteralOverride( + splashScreenStyleMethodMatch[0], + "$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..bc4c213cf0 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 @@ -19,6 +19,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/AlternativeThumbnailsPatch;" +@Suppress("unused") val alternativeThumbnailsPatch = bytecodePatch( name = "Alternative thumbnails", description = "Adds options to replace video thumbnails using the DeArrow API or image captures from the video.", @@ -33,14 +34,14 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.thumbnails.alternativeThumbnailsPatch") val entries = "revanced_alt_thumbnail_options_entries" @@ -49,27 +50,27 @@ val alternativeThumbnailsPatch = bytecodePatch( ListPreference( key = "revanced_alt_thumbnail_home", entriesKey = entries, - entryValuesKey = values + entryValuesKey = values, ), ListPreference( key = "revanced_alt_thumbnail_subscription", entriesKey = entries, - entryValuesKey = values + entryValuesKey = values, ), ListPreference( key = "revanced_alt_thumbnail_library", entriesKey = entries, - entryValuesKey = values + entryValuesKey = values, ), ListPreference( key = "revanced_alt_thumbnail_player", entriesKey = entries, - entryValuesKey = values + entryValuesKey = values, ), ListPreference( key = "revanced_alt_thumbnail_search", entriesKey = entries, - entryValuesKey = values + entryValuesKey = values, ), NonInteractivePreference( "revanced_alt_thumbnail_dearrow_about", 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..566a9069c2 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 @@ -13,6 +13,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/BypassImageRegionRestrictionsPatch;" +@Suppress("unused") val bypassImageRegionRestrictionsPatch = bytecodePatch( name = "Bypass image region restrictions", description = "Adds an option to use a different host for user avatar and channel images " + @@ -27,14 +28,14 @@ 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", + ), ) - execute { + apply { addResources("youtube", "layout.thumbnails.bypassImageRegionRestrictionsPatch") PreferenceScreen.MISC.addPreferences( 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..e50b59c3d2 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 @@ -1,17 +1,18 @@ package app.revanced.patches.youtube.misc.announcements -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction 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.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/announcements/AnnouncementsPatch;" +@Suppress("unused") val announcementsPatch = bytecodePatch( name = "Announcements", description = "Adds an option to show announcements from ReVanced on app startup.", @@ -23,21 +24,21 @@ 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", + ), ) - execute { + apply { addResources("youtube", "misc.announcements.announcementsPatch") PreferenceScreen.MISC.addPreferences( SwitchPreference("revanced_announcements"), ) - mainActivityOnCreateFingerprint.method.addInstruction( + mainActivityOnCreateMethod.addInstruction( 0, "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->showAnnouncement(Landroid/app/Activity;)V", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/Fingerprints.kt index 5fb44f20fb..7afaec73c3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/Fingerprints.kt @@ -1,14 +1,13 @@ package app.revanced.patches.youtube.misc.audiofocus -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethod +import app.revanced.patcher.patch.BytecodePatchContext -internal val audioFocusChangeListenerFingerprint = fingerprint { - strings( - "AudioFocus DUCK", - "AudioFocus loss; Will lower volume", - ) -} +internal val BytecodePatchContext.audioFocusChangeListenerMethod by gettingFirstMethod( + "AudioFocus DUCK", + "AudioFocus loss; Will lower volume", +) -internal val audioFocusRequestBuilderFingerprint = fingerprint { - strings("Can't build an AudioFocusRequestCompat instance without a listener") -} +internal val BytecodePatchContext.audioFocusRequestBuilderMethod by gettingFirstMethod( + "Can't build an AudioFocusRequestCompat instance without a listener", +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatch.kt index 2f6317d6e3..22689c44ca 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatch.kt @@ -1,10 +1,10 @@ package app.revanced.patches.youtube.misc.audiofocus -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.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.SwitchPreference @@ -27,10 +27,10 @@ val pauseOnAudioInterruptPatch = bytecodePatch( compatibleWith( "com.google.android.youtube"( "20.14.43", - ) + ), ) - execute { + apply { addResources("youtube", "misc.audiofocus.pauseOnAudioInterruptPatch") PreferenceScreen.MISC.addPreferences( @@ -39,10 +39,9 @@ val pauseOnAudioInterruptPatch = bytecodePatch( // Hook the builder method that creates AudioFocusRequest. // At the start, set the willPauseWhenDucked field (b) to true if setting is enabled. - val builderMethod = audioFocusRequestBuilderFingerprint.method - val builderClass = builderMethod.definingClass + val builderClass = audioFocusRequestBuilderMethod.definingClass - builderMethod.addInstructionsWithLabels( + audioFocusRequestBuilderMethod.addInstructionsWithLabels( 0, """ invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldPauseOnAudioInterrupt()Z @@ -51,16 +50,16 @@ val pauseOnAudioInterruptPatch = bytecodePatch( const/4 v0, 0x1 iput-boolean v0, p0, $builderClass->b:Z """, - ExternalLabel("skip_override", builderMethod.getInstruction(0)), + ExternalLabel("skip_override", audioFocusRequestBuilderMethod.getInstruction(0)), ) // Also hook the audio focus change listener as a backup. - audioFocusChangeListenerFingerprint.method.addInstructions( + audioFocusChangeListenerMethod.addInstructions( 0, """ invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->overrideAudioFocusChange(I)I move-result p1 - """ + """, ) } } 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..6b39c04b5d 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 @@ -1,14 +1,12 @@ 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.extensions.getInstruction +import app.revanced.patcher.extensions.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.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,51 +23,47 @@ 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;" -val backgroundPlaybackPatch = bytecodePatch( +@Suppress("unused") +val removeBackgroundPlaybackRestrictionsPatch = bytecodePatch( name = "Remove background playback restrictions", description = "Removes restrictions on background playback, including playing kids videos in the background.", ) { dependsOn( - backgroundPlaybackResourcePatch, + resourceMappingPatch, + addResourcesPatch, sharedExtensionPatch, playerTypeHookPatch, videoInformationPatch, settingsPatch, - versionCheckPatch + 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", + ), ) - execute { + apply { addResources("youtube", "misc.backgroundplayback.backgroundPlaybackPatch") PreferenceScreen.SHORTS.addPreferences( SwitchPreference("revanced_shorts_disable_background_playback"), ) + prefBackgroundAndOfflineCategoryId = ResourceType.STRING["pref_background_and_offline_category"] + arrayOf( - backgroundPlaybackManagerFingerprint to "isBackgroundPlaybackAllowed", - backgroundPlaybackManagerShortsFingerprint to "isBackgroundShortsPlaybackAllowed", - ).forEach { (fingerprint, integrationsMethod) -> - fingerprint.method.apply { + backgroundPlaybackManagerMethod to "isBackgroundPlaybackAllowed", + backgroundPlaybackManagerShortsMethod to "isBackgroundShortsPlaybackAllowed", + ).forEach { (method, integrationsMethod) -> + method.apply { findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index -> val register = getInstruction(index).registerA @@ -85,29 +79,32 @@ val backgroundPlaybackPatch = bytecodePatch( } // Enable background playback option in YouTube settings - backgroundPlaybackSettingsFingerprint.originalMethod.apply { + backgroundPlaybackSettingsMethod.apply { val booleanCalls = instructions.withIndex().filter { it.value.getReference()?.returnType == "Z" } val settingsBooleanIndex = booleanCalls.elementAt(1).index - val settingsBooleanMethod by navigate(this).to(settingsBooleanIndex) + + val settingsBooleanMethod = navigate(this).to(settingsBooleanIndex).stop() settingsBooleanMethod.returnEarly(true) } // Force allowing background play for Shorts. - shortsBackgroundPlaybackFeatureFlagFingerprint.method.returnEarly(true) + shortsBackgroundPlaybackFeatureFlagMethod.returnEarly(true) // Force allowing background play for videos labeled for kids. - kidsBackgroundPlaybackPolicyControllerFingerprint.method.returnEarly() + kidsBackgroundPlaybackPolicyControllerMethod.returnEarly() // 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 - ) + pipInputConsumerFeatureFlagMethodMatch.let { + it.method.insertLiteralOverride( + it[0], + 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..64d6a26d2d 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 @@ -1,14 +1,15 @@ package app.revanced.patches.youtube.misc.backgroundplayback -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val backgroundPlaybackManagerFingerprint = fingerprint { +internal val BytecodePatchContext.backgroundPlaybackManagerMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("L") + returnType("Z") + parameterTypes("L") opcodes( Opcode.CONST_4, Opcode.IF_EQZ, @@ -38,10 +39,10 @@ internal val backgroundPlaybackManagerFingerprint = fingerprint { ) } -internal val backgroundPlaybackSettingsFingerprint = fingerprint { +internal val BytecodePatchContext.backgroundPlaybackSettingsMethod by gettingFirstImmutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters() + returnType("Ljava/lang/String;") + parameterTypes() opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, @@ -54,10 +55,10 @@ internal val backgroundPlaybackSettingsFingerprint = fingerprint { literal { prefBackgroundAndOfflineCategoryId } } -internal val kidsBackgroundPlaybackPolicyControllerFingerprint = fingerprint { +internal val BytecodePatchContext.kidsBackgroundPlaybackPolicyControllerMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("I", "L", "L") + returnType("V") + parameterTypes("I", "L", "L") opcodes( Opcode.CONST_4, Opcode.IF_NE, @@ -71,23 +72,24 @@ internal val kidsBackgroundPlaybackPolicyControllerFingerprint = fingerprint { literal { 5 } } -internal val backgroundPlaybackManagerShortsFingerprint = fingerprint { +internal val BytecodePatchContext.backgroundPlaybackManagerShortsMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("L") - literal { 151635310 } + returnType("Z") + parameterTypes("L") + instructions(151635310L()) } -internal val shortsBackgroundPlaybackFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.shortsBackgroundPlaybackFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - literal { 45415425 } + returnType("Z") + parameterTypes() + instructions(45415425L()) } -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} -} \ No newline at end of file +internal val BytecodePatchContext.pipInputConsumerFeatureFlagMethodMatch by composingFirstMethod { + instructions( + // PiP input consumer feature flag. + 45638483L(), + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt index 9624721bbd..ffb2451231 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt @@ -2,10 +2,10 @@ package app.revanced.patches.youtube.misc.check import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod internal val checkEnvironmentPatch = checkEnvironmentPatch( - mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + getMainActivityOnCreateMethod = { mainActivityOnCreateMethod }, extensionPatch = sharedExtensionPatch, "com.google.android.youtube", ) 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 aa9632f250..73e5f30e78 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 @@ -11,10 +11,10 @@ val enableDebuggingPatch = enableDebuggingPatch( settingsPatch = settingsPatch, compatibleWithPackages = arrayOf( "com.google.android.youtube" to setOf( - "19.34.42", - "20.07.39", - "20.13.41", + "19.43.41", "20.14.43", + "20.21.37", + "20.31.40", ) ), hookStringFeatureFlag = true, 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..26f87fda7e 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,15 @@ package app.revanced.patches.youtube.misc.dimensions.spoof -import app.revanced.patcher.fingerprint +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType -internal val deviceDimensionsModelToStringFingerprint = fingerprint { - returns("L") - strings("minh.", ";maxh.") +internal val BytecodePatchContext.deviceDimensionsModelToStringMethod by gettingFirstMethodDeclaratively { + returnType("L") + instructions( + "minh."(), + ";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..934e2049bd 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 @@ -1,6 +1,8 @@ package app.revanced.patches.youtube.misc.dimensions.spoof -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.firstMethod import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -12,6 +14,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/spoof/SpoofDeviceDimensionsPatch;" +@Suppress("unused") val spoofDeviceDimensionsPatch = bytecodePatch( name = "Spoof device dimensions", description = "Adds an option to spoof the device dimensions which can unlock higher video qualities.", @@ -24,36 +27,34 @@ 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", + ), ) - execute { + apply { addResources("youtube", "misc.dimensions.spoof.spoofDeviceDimensionsPatch") PreferenceScreen.MISC.addPreferences( SwitchPreference("revanced_spoof_device_dimensions"), ) - deviceDimensionsModelToStringFingerprint - .classDef.methods.first { method -> method.name == "" } - // Override the parameters containing the dimensions. - .addInstructions( - 1, // Add after super call. - mapOf( - 1 to "MinHeightOrWidth", // p1 = min height - 2 to "MaxHeightOrWidth", // p2 = max height - 3 to "MinHeightOrWidth", // p3 = min width - 4 to "MaxHeightOrWidth", // p4 = max width - ).map { (parameter, method) -> - """ - invoke-static { p$parameter }, $EXTENSION_CLASS_DESCRIPTOR->get$method(I)I - move-result p$parameter - """ - }.joinToString("\n") { it }, - ) + // Override the parameters containing the dimensions. + deviceDimensionsModelToStringMethod.classDef.methods.firstMethod { name == "" }.addInstructions( + 1, // Add after super call. + arrayOf( + 1 to "MinHeightOrWidth", // p1 = min height + 2 to "MaxHeightOrWidth", // p2 = max height + 3 to "MinHeightOrWidth", // p3 = min width + 4 to "MaxHeightOrWidth", // p4 = max width + ).map { (parameter, method) -> + """ + invoke-static { p$parameter }, $EXTENSION_CLASS_DESCRIPTOR->get$method(I)I + move-result p$parameter + """ + }.joinToString("\n") { it }, + ) } } 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..2824056d46 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 @@ -1,23 +1,24 @@ package app.revanced.patches.youtube.misc.dns +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patches.shared.misc.dns.checkWatchHistoryDomainNameResolutionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod val checkWatchHistoryDomainNameResolutionPatch = checkWatchHistoryDomainNameResolutionPatch( block = { dependsOn( - sharedExtensionPatch + sharedExtensionPatch, ) 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", + ), ) }, - mainActivityFingerprint = mainActivityOnCreateFingerprint + getMainActivityMethod = BytecodePatchContext::mainActivityOnCreateMethod::get, ) 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..aaeb0eaa4f 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,8 @@ package app.revanced.patches.youtube.misc.extension.hooks +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +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 +13,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( + "Application.onCreate"(), + "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..f3f466007a 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,50 +1,31 @@ package app.revanced.patches.youtube.misc.fix.backtoexitgesture -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val scrollPositionFingerprint = fingerprint { +internal val BytecodePatchContext.scrollPositionMethodMatch by composingFirstMethod("scroll_position") { accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) - returns("V") - parameters("L") + returnType("V") + parameterTypes("L") opcodes( Opcode.IF_NEZ, Opcode.INVOKE_DIRECT, - Opcode.RETURN_VOID + Opcode.RETURN_VOID, ) - strings("scroll_position") } -/** - * Resolves using class found in [recyclerViewTopScrollingParentFingerprint]. - */ -internal val recyclerViewTopScrollingFingerprint = fingerprint { +internal val BytecodePatchContext.recyclerViewTopScrollingMethodMatch by composingFirstMethod { 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 + returnType("V") + parameterTypes() + instructions( + method { toString() == "Ljava/util/Iterator;->next()Ljava/lang/Object;" }, + after(Opcode.MOVE_RESULT_OBJECT()), + after(allOf(Opcode.CHECK_CAST(), type("Landroid/support/v7/widget/RecyclerView;"))), + after(0L()), + after(method { definingClass == "Landroid/support/v7/widget/RecyclerView;" }), + after(Opcode.GOTO()), ) } 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..a8615c1d50 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 @@ -1,8 +1,9 @@ package app.revanced.patches.youtube.misc.fix.backtoexitgesture -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.shared.mainActivityOnBackPressedFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnBackPressedMethod +import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode @@ -11,40 +12,36 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/FixBackToExitGesturePatch;" internal val fixBackToExitGesturePatch = bytecodePatch( - description = "Fixes the swipe back to exit gesture." + description = "Fixes the swipe back to exit gesture.", ) { - execute { - recyclerViewTopScrollingFingerprint.match( - recyclerViewTopScrollingParentFingerprint.originalClassDef - ).let { - it.method.addInstruction( - it.patternMatch!!.endIndex, - "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onTopView()V" + apply { + with(recyclerViewTopScrollingMethodMatch) { + method.addInstructionsAtControlFlowLabel( + recyclerViewTopScrollingMethodMatch[-1] + 1, + "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onTopView()V", ) } - scrollPositionFingerprint.let { - navigate(it.originalMethod) - .to(it.patternMatch!!.startIndex + 1) - .stop().apply { - val index = indexOfFirstInstructionOrThrow { - opcode == Opcode.INVOKE_VIRTUAL && getReference()?.definingClass == - "Landroid/support/v7/widget/RecyclerView;" - } - - addInstruction( - index, - "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onScrollingViews()V" - ) + with(scrollPositionMethodMatch) { + navigate(immutableMethod).to(scrollPositionMethodMatch[0] + 1).stop().apply { + val index = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.definingClass == + "Landroid/support/v7/widget/RecyclerView;" } + + addInstruction( + index, + "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onScrollingViews()V", + ) + } } - mainActivityOnBackPressedFingerprint.method.apply { + mainActivityOnBackPressedMethod.apply { val index = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID) addInstruction( index, - "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V" + "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)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..e97c6725d5 --- /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.addInstruction +import app.revanced.patcher.extensions.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, + ) + + apply { + unstableContentProviderMethodMatch.let { + val insertIndex = it[0] + + it.method.apply { + val register = getInstruction(insertIndex).registerD + + addInstruction( + insertIndex, + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->removeNullMapEntries(Ljava/util/Map;)V", + ) + } + } + } +} 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..ac8772d051 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/contentprovider/Fingerprints.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.misc.fix.contentprovider + +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import com.android.tools.smali.dexlib2.AccessFlags + +internal val BytecodePatchContext.unstableContentProviderMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("Landroid/content/ContentResolver;", "[Ljava/lang/String;") + instructions( + // Early targets use HashMap and later targets use ConcurrentMap. + method { name == "putAll" && parameterTypes.count() == 1 && parameterTypes.first() == "Ljava/util/Map;" }, + "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/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/Fingerprints.kt index d5a255ca5f..728ad0fef0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/Fingerprints.kt @@ -1,6 +1,13 @@ package app.revanced.patches.youtube.misc.fix.playbackspeed -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.custom +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionReversed import com.android.tools.smali.dexlib2.AccessFlags @@ -12,10 +19,10 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference * This method is usually used to set the initial speed (1.0x) when playback starts from the feed. * For some reason, in the latest YouTube, it is invoked even after the video has already started. */ -internal val playbackSpeedInFeedsFingerprint = fingerprint { +internal val BytecodePatchContext.playbackSpeedInFeedsMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") + returnType("V") + parameterTypes("L") opcodes( Opcode.IGET, Opcode.MUL_INT_LIT16, @@ -26,13 +33,12 @@ internal val playbackSpeedInFeedsFingerprint = fingerprint { Opcode.IF_LEZ, Opcode.SUB_LONG_2ADDR, ) - custom { method, _ -> - indexOfGetPlaybackSpeedInstruction(method) >= 0 + custom { + indexOfGetPlaybackSpeedInstruction(this) >= 0 } } -internal fun indexOfGetPlaybackSpeedInstruction(method: Method) = - method.indexOfFirstInstructionReversed { - opcode == Opcode.IGET && - getReference()?.type == "F" - } +internal fun indexOfGetPlaybackSpeedInstruction(method: Method) = method.indexOfFirstInstructionReversed { + opcode == Opcode.IGET && + getReference()?.type == "F" +} 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 similarity index 84% rename from patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FixPlaybackSpeedWhilePlayingPatch.kt index b63eec7941..a1b00e22e0 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 @@ -1,9 +1,9 @@ package app.revanced.patches.youtube.misc.fix.playbackspeed -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel 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_34_or_greater @@ -29,20 +29,19 @@ private const val EXTENSION_CLASS_DESCRIPTOR = * 6. Resume the video * 7. Playback speed will incorrectly change to 1.0x. */ -@Suppress("unused") -val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{ +val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch { dependsOn( sharedExtensionPatch, playerTypeHookPatch, versionCheckPatch, ) - execute { + apply { if (!is_19_34_or_greater) { - return@execute + return@apply } - playbackSpeedInFeedsFingerprint.method.apply { + playbackSpeedInFeedsMethod.apply { val playbackSpeedIndex = indexOfGetPlaybackSpeedInstruction(this) val playbackSpeedRegister = getInstruction(playbackSpeedIndex).registerA val returnIndex = indexOfFirstInstructionOrThrow(playbackSpeedIndex, Opcode.RETURN_VOID) @@ -56,8 +55,8 @@ val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{ move-result v$freeRegister if-nez v$freeRegister, :do_not_change """, - ExternalLabel("do_not_change", getInstruction(returnIndex)) + ExternalLabel("do_not_change", getInstruction(returnIndex)), ) } } -} \ No newline at end of file +} 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..6b07ea5bed 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 @@ -1,50 +1,23 @@ 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.extensions.addInstructions +import app.revanced.patcher.extensions.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 + addResourcesPatch, ) - execute { + apply { addResources("youtube", "misc.gms.accountCredentialsInvalidTextPatch") // If the user recently changed their account password, @@ -57,27 +30,19 @@ internal val accountCredentialsInvalidTextPatch = bytecodePatch { // MicroG accounts look almost identical to Google device accounts // and it's more foolproof to instead uninstall/reinstall. arrayOf( - 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 + specificNetworkErrorViewControllerMethodMatch, + loadingFrameLayoutControllerMethodMatch, + ).forEach { match -> + val index = match[-1] + val register = match.method.getInstruction(index).registerA - addInstructions( - getStringIndex + 2, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getOfflineNetworkErrorString(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$register - """ - ) - } + match.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..78ab4b951d 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,27 +1,33 @@ package app.revanced.patches.youtube.misc.gms -import app.revanced.patcher.fingerprint -import app.revanced.util.containsLiteralInstruction +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode -internal val specificNetworkErrorViewControllerFingerprint = fingerprint { +internal val BytecodePatchContext.specificNetworkErrorViewControllerMethodMatch by composingFirstMethod { 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) - } + returnType("V") + parameterTypes() + instructions( + ResourceType.DRAWABLE("ic_offline_no_content_upside_down"), + ResourceType.STRING("offline_no_content_body_text_not_offline_eligible"), + method { name == "getString" && returnType == "Ljava/lang/String;" }, + after(Opcode.MOVE_RESULT_OBJECT()), + ) } // It's not clear if this second class is ever used and it may be dead code, -// but it the layout image/text is identical to the network error fingerprint above. -internal val loadingFrameLayoutControllerFingerprint = fingerprint { +// but it the layout image/text is identical to the network error match above. +internal val BytecodePatchContext.loadingFrameLayoutControllerMethodMatch by composingFirstMethod { 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) - } -} \ No newline at end of file + returnType("V") + parameterTypes("L") + instructions( + ResourceType.DRAWABLE("ic_offline_no_content_upside_down"), + ResourceType.STRING("offline_no_content_body_text_not_offline_eligible"), + method { name == "getString" && returnType == "Ljava/lang/String;" }, + after(Opcode.MOVE_RESULT_OBJECT()), + ) +} 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 605f904479..e745c28381 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 @@ -1,14 +1,15 @@ package app.revanced.patches.youtube.misc.gms +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.Option import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch -import app.revanced.patches.shared.castContextFetchFingerprint +import app.revanced.patches.shared.castContextFetchMethod import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch import app.revanced.patches.shared.misc.settings.preference.IntentPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.shared.primeMethodFingerprint +import app.revanced.patches.shared.primeMethod import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME @@ -16,17 +17,15 @@ import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.spoof.spoofVideoStreamsPatch -import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod @Suppress("unused") val gmsCoreSupportPatch = gmsCoreSupportPatch( fromPackageName = YOUTUBE_PACKAGE_NAME, toPackageName = REVANCED_YOUTUBE_PACKAGE_NAME, - primeMethodFingerprint = primeMethodFingerprint, - earlyReturnFingerprints = setOf( - castContextFetchFingerprint, - ), - mainActivityOnCreateFingerprintToInsertIndex = mainActivityOnCreateFingerprint to { 0 }, + getPrimeMethod = BytecodePatchContext::primeMethod::get, + getEarlyReturnMethods = setOf(BytecodePatchContext::castContextFetchMethod::get), + getMainActivityOnCreateMethodToGetInsertIndex = BytecodePatchContext::mainActivityOnCreateMethod::get to { 0 }, extensionPatch = sharedExtensionPatch, gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch, ) { @@ -37,10 +36,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..95f7b428e4 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 @@ -1,9 +1,9 @@ package app.revanced.patches.youtube.misc.hapticfeedback -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.PreferenceScreenPreference @@ -26,14 +26,14 @@ 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", + ), ) - execute { + apply { addResources("youtube", "misc.hapticfeedback.disableHapticFeedbackPatch") PreferenceScreen.PLAYER.addPreferences( @@ -44,28 +44,26 @@ val disableHapticFeedbackPatch = bytecodePatch( SwitchPreference("revanced_disable_haptic_feedback_precise_seeking"), SwitchPreference("revanced_disable_haptic_feedback_seek_undo"), SwitchPreference("revanced_disable_haptic_feedback_zoom"), - ) - ) + ), + ), ) arrayOf( - markerHapticsFingerprint to "disableChapterVibrate", - scrubbingHapticsFingerprint to "disablePreciseSeekingVibrate", - seekUndoHapticsFingerprint to "disableSeekUndoVibrate", - zoomHapticsFingerprint to "disableZoomVibrate" - ).forEach { (fingerprint, methodName) -> - fingerprint.method.apply { - addInstructionsWithLabels( - 0, - """ - invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->$methodName()Z - move-result v0 - if-eqz v0, :vibrate - return-void - """, - ExternalLabel("vibrate", getInstruction(0)) - ) - } + markerHapticsMethod to "disableChapterVibrate", + scrubbingHapticsMethod to "disablePreciseSeekingVibrate", + seekUndoHapticsMethod to "disableSeekUndoVibrate", + zoomHapticsMethod to "disableZoomVibrate", + ).forEach { (method, methodName) -> + method.addInstructionsWithLabels( + 0, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->$methodName()Z + move-result v0 + if-eqz v0, :vibrate + return-void + """, + ExternalLabel("vibrate", method.getInstruction(0)), + ) } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/Fingerprints.kt index 13efc46931..f37729e3d1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/hapticfeedback/Fingerprints.kt @@ -1,23 +1,28 @@ package app.revanced.patches.youtube.misc.hapticfeedback -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val markerHapticsFingerprint = fingerprint { - returns("V") - strings("Failed to execute markers haptics vibrate.") +internal val BytecodePatchContext.markerHapticsMethod by gettingFirstMethodDeclaratively( + "Failed to execute markers haptics vibrate.", +) { + returnType("V") } -internal val scrubbingHapticsFingerprint = fingerprint { - returns("V") - strings("Failed to haptics vibrate for fine scrubbing.") +internal val BytecodePatchContext.scrubbingHapticsMethod by gettingFirstMethodDeclaratively( + "Failed to haptics vibrate for fine scrubbing.", +) { + returnType("V") } -internal val seekUndoHapticsFingerprint = fingerprint { - returns("V") - strings("Failed to execute seek undo haptics vibrate.") +internal val BytecodePatchContext.seekUndoHapticsMethod by gettingFirstMethodDeclaratively( + "Failed to execute seek undo haptics vibrate.", +) { + returnType("V") } -internal val zoomHapticsFingerprint = fingerprint { - returns("V") - strings("Failed to haptics vibrate for video zoom") +internal val BytecodePatchContext.zoomHapticsMethod by gettingFirstMethodDeclaratively( + "Failed to haptics vibrate for video zoom", +) { + returnType("V") } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt index b9292ef84b..91b2192276 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt @@ -1,11 +1,13 @@ package app.revanced.patches.youtube.misc.imageurlhook -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.util.getReference import com.android.tools.smali.dexlib2.AccessFlags @@ -29,19 +31,15 @@ val cronetImageUrlHookPatch = bytecodePatch( ) { dependsOn(sharedExtensionPatch) - execute { - loadImageUrlMethod = messageDigestImageUrlFingerprint - .match(messageDigestImageUrlParentFingerprint.originalClassDef).method + apply { - loadImageSuccessCallbackMethod = onSucceededFingerprint - .match(onResponseStartedFingerprint.originalClassDef).method - - loadImageErrorCallbackMethod = onFailureFingerprint - .match(onResponseStartedFingerprint.originalClassDef).method + loadImageUrlMethod = messageDigestImageUrlParentMethod.immutableClassDef.getMessageDigestImageUrlMethod() + loadImageSuccessCallbackMethod = onResponseStartedMethod.immutableClassDef.getOnSucceededMethod() + loadImageErrorCallbackMethod = onResponseStartedMethod.immutableClassDef.getOnFailureMethod() // The URL is required for the failure callback hook, but the URL field is obfuscated. // Add a helper get method that returns the URL field. - val urlFieldInstruction = requestFingerprint.method.instructions.first { + val urlFieldInstruction = requestMethod.instructions.first { val reference = it.getReference() it.opcode == Opcode.IPUT_OBJECT && reference?.type == "Ljava/lang/String;" } as ReferenceInstruction @@ -49,7 +47,7 @@ val cronetImageUrlHookPatch = bytecodePatch( val urlFieldName = (urlFieldInstruction.reference as FieldReference).name val definingClass = CRONET_URL_REQUEST_CLASS_DESCRIPTOR val addedMethodName = "getHookedUrl" - requestFingerprint.classDef.methods.add( + requestMethod.classDef.methods.add( ImmutableMethod( definingClass, addedMethodName, 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..29d69d9a80 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,64 +1,64 @@ package app.revanced.patches.youtube.misc.imageurlhook -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val onFailureFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getOnFailureMethod() = firstMethodDeclaratively { + name("onFailed") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters( + returnType("V") + parameterTypes( "Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;", - "Lorg/chromium/net/CronetException;" + "Lorg/chromium/net/CronetException;", ) - custom { method, _ -> - method.name == "onFailed" - } } -// Acts as a parent fingerprint. -internal val onResponseStartedFingerprint = fingerprint { +// Acts as a parent method. +internal val BytecodePatchContext.onResponseStartedMethod by gettingFirstMethodDeclaratively( + "Content-Length", + "Content-Type", + "identity", + "application/x-protobuf", +) { + name("onResponseStarted") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;") - strings( - "Content-Length", - "Content-Type", - "identity", - "application/x-protobuf", - ) - custom { method, _ -> - method.name == "onResponseStarted" - } + returnType("V") + parameterTypes("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;") } -internal val onSucceededFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getOnSucceededMethod() = firstMethodDeclaratively { + name("onSucceeded") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;") - custom { method, _ -> - method.name == "onSucceeded" - } + returnType("V") + parameterTypes("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;") } internal const val CRONET_URL_REQUEST_CLASS_DESCRIPTOR = "Lorg/chromium/net/impl/CronetUrlRequest;" -internal val requestFingerprint = fingerprint { +internal val BytecodePatchContext.requestMethod by gettingFirstMethodDeclaratively { + definingClass(CRONET_URL_REQUEST_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - custom { _, classDef -> - classDef.type == CRONET_URL_REQUEST_CLASS_DESCRIPTOR - } } -internal val messageDigestImageUrlFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getMessageDigestImageUrlMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters("Ljava/lang/String;", "L") + parameterTypes("Ljava/lang/String;", "L") } -internal val messageDigestImageUrlParentFingerprint = fingerprint { +internal val BytecodePatchContext.messageDigestImageUrlParentMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/String;") - parameters() - strings("@#&=*+-_.,:!?()/~'%;\$") + returnType("Ljava/lang/String;") + parameterTypes() + instructions( + anyOf( + string { equals("@#&=*+-_.,:!?()/~'%;$") }, + string { equals("@#&=*+-_.,:!?()/~'%;$[]") }, // 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..99f4a9f2cf 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 @@ -1,24 +1,20 @@ package app.revanced.patches.youtube.misc.links -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.replaceInstruction 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_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;" + +@Suppress("unused") val bypassURLRedirectsPatch = bytecodePatch( name = "Bypass URL redirects", description = "Adds an option to bypass URL redirects and open the original URL directly.", @@ -27,60 +23,40 @@ 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", + ), ) - execute { + apply { addResources("youtube", "misc.links.bypassURLRedirectsPatch") PreferenceScreen.MISC.addPreferences( SwitchPreference("revanced_bypass_url_redirects"), ) - val fingerprints = if (is_19_33_or_greater) { - arrayOf( - abUriParserFingerprint, - httpUriParserFingerprint, - ) - } else { - arrayOf( - abUriParserLegacyFingerprint, - httpUriParserLegacyFingerprint, - ) - } + arrayOf( + if (is_20_37_or_greater) { + (abUriParserMethodMatch to 2) + } else { + (abUriParserLegacyMethodMatch to 2) + }, + httpUriParserMethodMatch to 0, + ).forEach { (match, index) -> + val insertIndex = match[index] + val uriStringRegister = match.method.getInstruction(insertIndex).registerC - fingerprints.forEach { - it.method.apply { - val insertIndex = findUriParseIndex() - val uriStringRegister = getInstruction(insertIndex).registerC - - replaceInstruction( - insertIndex, - "invoke-static {v$uriStringRegister}," + - "Lapp/revanced/extension/youtube/patches/BypassURLRedirectsPatch;" + - "->" + - "parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;", - ) - } + match.method.replaceInstruction( + insertIndex, + "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..bea03da447 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,48 @@ package app.revanced.patches.youtube.misc.links -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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 BytecodePatchContext.abUriParserLegacyMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Ljava/lang/Object") - parameters("Ljava/lang/Object") - custom { method, _ -> - method.findUriParseIndex() >= 0 && method.findWebViewCheckCastIndex() >= 0 - } + returnType("Ljava/lang/Object;") + parameterTypes("Ljava/lang/Object;") + instructions( + "Found entityKey=`"(), + "that does not contain a PlaylistVideoEntityId"(String::contains), + method { toString() == "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 BytecodePatchContext.abUriParserMethodMatch by composingFirstMethod { 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, + returnType("Ljava/lang/Object;") + parameterTypes("Ljava/lang/Object;") + instructions( + // Method is a switch statement of unrelated code, + // and there's no strings or anything unique to match to. + method { toString() == "Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;" }, + method { toString() == "Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;" }, + method { toString() == "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;" }, + method { toString() == "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 BytecodePatchContext.httpUriParserMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Landroid/net/Uri") - parameters("Ljava/lang/String") - opcodes( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, + returnType("Landroid/net/Uri;") + parameterTypes("Ljava/lang/String;") + instructions( + method { toString() == "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;" }, + "https"(), + "://"(), + "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..6132801071 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 @@ -1,6 +1,6 @@ package app.revanced.patches.youtube.misc.links -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.transformation.transformInstructionsPatch @@ -10,6 +10,7 @@ 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.StringReference +@Suppress("unused") val openLinksExternallyPatch = bytecodePatch( name = "Open links externally", description = "Adds an option to always open links in your browser instead of the in-app browser.", @@ -41,14 +42,14 @@ 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", + ), ) - execute { + apply { addResources("youtube", "misc.links.openLinksExternallyPatch") PreferenceScreen.MISC.addPreferences( 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 9c97c58816..aab6d49715 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.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode -internal val lithoComponentNameUpbFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.lithoComponentNameUpbFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - literal { 45631264L } + returnType("Z") + parameterTypes() + instructions(45631264L()) } -internal val lithoConverterBufferUpbFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("L") - parameters("L") - literal { 45419603L } +internal val BytecodePatchContext.lithoConverterBufferUpbFeatureFlagMethodMatch by composingFirstMethod { + returnType("L") + instructions(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 f2a7dce18c..be87c62780 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 @@ -2,69 +2,73 @@ package app.revanced.patches.youtube.misc.litho.filter -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.youtube.shared.conversionContextToStringMethod +import app.revanced.patches.shared.misc.litho.filter.EXTENSION_CLASS_DESCRIPTOR import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch -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.versionCheckPatch -import app.revanced.patches.youtube.shared.conversionContextFingerprintToString +import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceLegacyMethod +import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceMethodMatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.* import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.insertLiteralOverride +import app.revanced.util.returnLate import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -@Deprecated("Use the shared one instead", ReplaceWith("app.revanced.patches.shared.misc.litho.filter.addLithoFilter")) -lateinit var addLithoFilter: (String) -> Unit - private set val lithoFilterPatch = lithoFilterPatch( componentCreateInsertionIndex = { if (is_19_17_or_greater) { indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) } else { - // 19.16 clobbers p2 so must check at start of the method + // 19.16 clobbers p2 so must check at start of the method and not at the return index. 0 } }, - conversionContextFingerprintToString = conversionContextFingerprintToString, - executeBlock = BytecodePatchContext::executeBlock, -) { - dependsOn(versionCheckPatch) -} - -private fun BytecodePatchContext.executeBlock() { - // region A/B test of new Litho native code. - - // Turn off native code that handles litho component names. If this feature is on then nearly - // all litho components have a null name and identifier/path filtering is completely broken. - // - // 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") + insertProtobufHook = { + 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. + protobufBufferReferenceMethodMatch.let { + // Hook the buffer after the call to jniDecode(). + it.method.addInstruction( + it[-1] + 1, + "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer([B)V", + ) + } } + + // Legacy non-native buffer. + protobufBufferReferenceLegacyMethod.addInstruction( + 0, + "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", + ) + }, + getConversionContextToStringMethod = BytecodePatchContext::conversionContextToStringMethod::get, + getExtractIdentifierFromBuffer = { is_20_21_or_greater }, + executeBlock = { + // region A/B test of new Litho native code. + + // Turn off native code that handles litho component names. If this feature is on then nearly + // all litho components have a null name and identifier/path filtering is completely broken. + // + // 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) { + lithoComponentNameUpbFeatureFlagMethod.returnLate(false) + } + + // Turn off a feature flag that enables native code of protobuf parsing (Upb protobuf). + lithoConverterBufferUpbFeatureFlagMethodMatch.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[0], + false, + ) + } + // endregion } - - // 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") - } - - // endregion - - // Set the addLithoFilter function to the one from the shared patch. - // This is done for backwards compatibility. - addLithoFilter = app.revanced.patches.shared.misc.litho.filter.addLithoFilter +) { + dependsOn(sharedExtensionPatch, versionCheckPatch) } 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..388a54e5d5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.youtube.misc.loopvideo + +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import com.android.tools.smali.dexlib2.AccessFlags + +internal val BytecodePatchContext.videoStartPlaybackMethod by gettingFirstImmutableMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + instructions( + "play() called when the player wasn't loaded."(), + "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..1c70aa973c 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,14 +7,15 @@ 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 private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/LoopVideoPatch;" +@Suppress("ObjectPropertyName") val loopVideoPatch = bytecodePatch( name = "Loop video", description = "Adds an option to loop videos and display loop video button in the video player.", @@ -22,28 +23,30 @@ 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", + ), ) - execute { + apply { addResources("youtube", "misc.loopvideo.loopVideoPatch") PreferenceScreen.MISC.addPreferences( 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,10 +54,11 @@ 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 }, $videoStartPlaybackMethod + return-void :do_not_loop nop - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/button/LoopVideoButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/button/LoopVideoButtonPatch.kt index 0c3b9dc68b..62ce94750c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/button/LoopVideoButtonPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/loopvideo/button/LoopVideoButtonPatch.kt @@ -15,7 +15,7 @@ import app.revanced.util.copyResources private val loopVideoButtonResourcePatch = resourcePatch { dependsOn(playerControlsResourcePatch) - execute { + apply { copyResources( "loopvideobutton", ResourceGroup( @@ -43,7 +43,7 @@ internal val loopVideoButtonPatch = bytecodePatch( playerControlsPatch, ) - execute { + apply { addResources("youtube", "misc.loopvideo.button.loopVideoButtonPatch") PreferenceScreen.PLAYER.addPreferences( 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..b1a3dbc82f 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,123 +1,147 @@ package app.revanced.patches.youtube.misc.navigation -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.instructions +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import app.revanced.patches.shared.misc.mapping.ResourceType 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 +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val actionBarSearchResultsFingerprint = fingerprint { +internal val BytecodePatchContext.actionBarSearchResultsMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/view/View;") - literal { actionBarSearchResultsViewMicId } + returnType("Landroid/view/View;") + instructions( + ResourceType.LAYOUT("action_bar_search_results_view_mic"), + method("setLayoutDirection"), + ) } -internal val toolbarLayoutFingerprint = fingerprint { +internal val BytecodePatchContext.toolbarLayoutMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PROTECTED, AccessFlags.CONSTRUCTOR) - literal { toolbarContainerId } + instructions( + ResourceType.ID("toolbar_container"), + allOf( + Opcode.CHECK_CAST(), + type("Lcom/google/android/apps/youtube/app/ui/actionbar/MainCollapsingToolbarLayout;"), + ), + ) } /** * Matches to https://android.googlesource.com/platform/frameworks/support/+/9eee6ba/v7/appcompat/src/android/support/v7/widget/Toolbar.java#963 */ -internal val appCompatToolbarBackButtonFingerprint = fingerprint { +internal val BytecodePatchContext.appCompatToolbarBackButtonMethod by gettingFirstImmutableMethodDeclaratively { + definingClass("Landroid/support/v7/widget/Toolbar;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/graphics/drawable/Drawable;") - parameters() - custom { methodDef, classDef -> - classDef.type == "Landroid/support/v7/widget/Toolbar;" - } + returnType("Landroid/graphics/drawable/Drawable;") + parameterTypes() } /** - * Matches to the class found in [pivotBarConstructorFingerprint]. + * Matches to the class found in [pivotBarConstructorMethod]. */ -internal val initializeButtonsFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getInitializeButtonsMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - literal { imageOnlyTabResourceId } + returnType("V") + instructions(ResourceType.LAYOUT("image_only_tab")) } /** * Extension method, used for callback into to other patches. * Specifically, [navigationButtonsPatch]. */ -internal val navigationBarHookCallbackFingerprint = fingerprint { +internal val BytecodePatchContext.navigationBarHookCallbackMethod by gettingFirstMethodDeclaratively { + name("navigationTabCreatedCallback") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("V") - parameters(EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR, "Landroid/view/View;") - custom { method, _ -> - method.name == "navigationTabCreatedCallback" && - method.definingClass == EXTENSION_CLASS_DESCRIPTOR - } + returnType("V") + parameterTypes(EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR, "Landroid/view/View;") } /** * Matches to the Enum class that looks up ordinal -> instance. */ -internal val navigationEnumFingerprint = fingerprint { +internal val BytecodePatchContext.navigationEnumMethod by gettingFirstImmutableMethodDeclaratively( + "PIVOT_HOME", + "TAB_SHORTS", + "CREATION_TAB_LARGE", + "PIVOT_SUBSCRIPTIONS", + "TAB_ACTIVITY", + "VIDEO_LIBRARY_WHITE", + "INCOGNITO_CIRCLE", + "UNKNOWN", // Required to distinguish from patch extension class. +) { accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - strings( - "PIVOT_HOME", - "TAB_SHORTS", - "CREATION_TAB_LARGE", - "PIVOT_SUBSCRIPTIONS", - "TAB_ACTIVITY", - "VIDEO_LIBRARY_WHITE", - "INCOGNITO_CIRCLE", +} + +internal val BytecodePatchContext.pivotBarButtonsCreateDrawableViewMethod by gettingFirstImmutableMethodDeclaratively { + definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Landroid/view/View;") + custom { + // Only one view creation method has a Drawable parameter. + parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;" + } +} + +internal val BytecodePatchContext.pivotBarButtonsCreateResourceStyledViewMethod by gettingFirstImmutableMethodDeclaratively { + definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Landroid/view/View;") + parameterTypes("L", "Z", "I", "L") +} + +/** + * 20.21+ + */ +internal val BytecodePatchContext.pivotBarButtonsCreateResourceIntViewMethod by gettingFirstImmutableMethodDeclaratively { + definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Landroid/view/View;") + custom { + // Only one view creation method has an int first parameter. + parameterTypes.firstOrNull() == "I" + } +} + +internal val BytecodePatchContext.pivotBarButtonsViewSetSelectedMethodMatch by composingFirstMethod { + definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("I", "Z") + instructions(method("setSelected")) +} + +internal val BytecodePatchContext.pivotBarConstructorMethod by gettingFirstImmutableMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + instructions("com.google.android.apps.youtube.app.endpoint.flags"()) +} + +internal val BytecodePatchContext.imageEnumConstructorMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + instructions( + "TAB_ACTIVITY_CAIRO"(), + after(Opcode.INVOKE_DIRECT()), + after(Opcode.SPUT_OBJECT()), ) } -internal val pivotBarButtonsCreateDrawableViewFingerprint = 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 method has a Drawable parameter. - method.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;" - } -} - -internal val pivotBarButtonsCreateResourceViewFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/view/View;") - parameters("L", "Z", "I", "L") - custom { method, _ -> - method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" - } -} - -internal fun indexOfSetViewSelectedInstruction(method: Method) = method.indexOfFirstInstruction { - opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setSelected" -} - -internal val pivotBarButtonsViewSetSelectedFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("I", "Z") - custom { method, _ -> - indexOfSetViewSelectedInstruction(method) >= 0 && - 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") -} - -internal val imageEnumConstructorFingerprint = fingerprint { - accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) - strings("TAB_ACTIVITY_CAIRO") -} - -internal val setEnumMapFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - literal { - ytFillBellId - } +internal val BytecodePatchContext.setEnumMapMethodMatch by composingFirstMethod { + instructions( + ResourceType.DRAWABLE("yt_fill_bell_black_24"), + afterAtMost( + 10, + method { toString() == "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;" }), + afterAtMost( + 10, + method { toString() == "Ljava/util/EnumMap;->put(Ljava/lang/Enum;Ljava/lang/Object;)Ljava/lang/Object;" }, + ), + ) } 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..8e7651635c 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 @@ -1,25 +1,26 @@ package app.revanced.patches.youtube.misc.navigation -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.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.reference +import app.revanced.patcher.immutableClassDef 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.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.shared.mainActivityOnBackPressedFingerprint +import app.revanced.patches.youtube.misc.playservice.* +import app.revanced.patches.youtube.shared.mainActivityOnBackPressedMethod +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 @@ -27,49 +28,43 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction 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.instruction.formats.Instruction31i 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;" internal const val EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR = - "Lapp/revanced/extension/youtube/shared/NavigationBar\$NavigationButton;" + $$"Lapp/revanced/extension/youtube/shared/NavigationBar$NavigationButton;" private const val EXTENSION_TOOLBAR_INTERFACE = - "Lapp/revanced/extension/youtube/shared/NavigationBar${'$'}AppCompatToolbarPatchInterface;" + $$"Lapp/revanced/extension/youtube/shared/NavigationBar$AppCompatToolbarPatchInterface;" 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 to find methods + resourcePatch { + // Copy missing notification icon. + apply { + copyResources( + "navigationbuttons", + ResourceGroup( + "drawable", + "revanced_fill_bell_cairo_black_24.xml", + ), + ) + } + }, ) - execute { - fun MutableMethod.addHook(hook: Hook, insertPredicate: Instruction.() -> Boolean) { + apply { + 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 { @@ -84,34 +79,42 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig } } - initializeButtonsFingerprint.match(pivotBarConstructorFingerprint.originalClassDef).method.apply { + pivotBarConstructorMethod.immutableClassDef.getInitializeButtonsMethod().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) { + val navigationEnumClassName = navigationEnumMethod.classDef.type + 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@{ + val drawableTabMethod = pivotBarButtonsCreateDrawableViewMethod + 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) { + addHook(NavigationHook.NAVIGATION_TAB_LOADED) predicate@{ + MethodUtil.methodSignaturesMatch( + getReference() ?: return@predicate false, + pivotBarButtonsCreateResourceIntViewMethod, + ) + } + } + + addHook(NavigationHook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{ MethodUtil.methodSignaturesMatch( getReference() ?: return@predicate false, - imageResourceTabMethod, + pivotBarButtonsCreateResourceStyledViewMethod, ) } } - pivotBarButtonsViewSetSelectedFingerprint.method.apply { - val index = indexOfSetViewSelectedInstruction(this) + pivotBarButtonsViewSetSelectedMethodMatch.method.apply { + val index = pivotBarButtonsViewSetSelectedMethodMatch[0] val instruction = getInstruction(index) val viewRegister = instruction.registerC val isSelectedRegister = instruction.registerD @@ -125,7 +128,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig // Hook onto back button pressed. Needed to fix race problem with // Litho filtering based on navigation tab before the tab is updated. - mainActivityOnBackPressedFingerprint.method.addInstruction( + mainActivityOnBackPressedMethod.addInstruction( 0, "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V", ) @@ -135,113 +138,104 @@ 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, - ) + actionBarSearchResultsMethodMatch.let { + it.method.apply { + val instructionIndex = it[-1] + val viewRegister = getInstruction(instructionIndex).registerC - val instructionIndex = indexOfFirstInstructionOrThrow(searchBarResourceId) { - opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setLayoutDirection" - } - - 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 - - addInstruction( - index + 1, - "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setToolbar(Landroid/widget/FrameLayout;)V" - ) - } - - // Add interface for extensions code to call obfuscated methods. - appCompatToolbarBackButtonFingerprint.let { - it.classDef.apply { - interfaces.add(EXTENSION_TOOLBAR_INTERFACE) - - val definingClass = type - val obfuscatedMethodName = it.originalMethod.name - val returnType = "Landroid/graphics/drawable/Drawable;" - - methods.add( - ImmutableMethod( - definingClass, - "patch_getNavigationIcon", - listOf(), - returnType, - AccessFlags.PUBLIC.value or AccessFlags.FINAL.value, - null, - null, - MutableMethodImplementation(2), - ).toMutable().apply { - addInstructions( - 0, - """ - invoke-virtual { p0 }, $definingClass->$obfuscatedMethodName()$returnType - move-result-object v0 - return-object v0 - """ - ) - } + addInstruction( + instructionIndex, + "invoke-static { v$viewRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V", ) } } + // Hook the back button visibility. + + toolbarLayoutMethodMatch.let { + it.method.apply { + val index = it[-1] + val register = getInstruction(index).registerA + + addInstruction( + index + 1, + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setToolbar(Landroid/widget/FrameLayout;)V", + ) + } + } + + // Add interface for extensions code to call obfuscated methods. + appCompatToolbarBackButtonMethod.classDef.apply { + interfaces.add(EXTENSION_TOOLBAR_INTERFACE) + + val definingClass = type + val obfuscatedMethodName = appCompatToolbarBackButtonMethod.name + val returnType = "Landroid/graphics/drawable/Drawable;" + + methods.add( + ImmutableMethod( + definingClass, + "patch_getNavigationIcon", + listOf(), + returnType, + AccessFlags.PUBLIC.value or AccessFlags.FINAL.value, + null, + null, + MutableMethodImplementation(2), + ).toMutable().apply { + addInstructions( + 0, + """ + invoke-virtual { p0 }, $definingClass->$obfuscatedMethodName()$returnType + move-result-object v0 + return-object v0 + """, + ) + }, + ) + } + hookNavigationButtonCreated = { extensionClassDescriptor -> - navigationBarHookCallbackFingerprint.method.addInstruction( + navigationBarHookCallbackMethod.addInstruction( 0, "invoke-static { p0, p1 }, $extensionClassDescriptor->navigationTabCreated" + "(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V", ) } + if (is_20_39_or_greater) { + return@apply Logger.getLogger(this::class.java.name).warning( + "20.39+ Navigation tab activity button selected state is not yet fixed.", + ) + } + // 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_19_35_or_greater && !is_20_39_or_greater) { // FIXME: 20.39+ needs this fix. + val cairoNotificationEnumReference = + imageEnumConstructorMethodMatch.method.getInstruction(imageEnumConstructorMethodMatch[-1]).reference - 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;" - } - val instruction = getInstruction(enumMapIndex) + setEnumMapMethodMatch.apply { + val setEnumIntegerIndex = setEnumMapMethodMatch[-1] + method.apply { + val enumMapRegister = getInstruction(setEnumIntegerIndex).registerC + val insertIndex = setEnumIntegerIndex + 1 + val freeRegister = findFreeRegister(insertIndex, enumMapRegister) - 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 - """ - ) + addInstructions( + insertIndex, + """ + sget-object v$freeRegister, $cairoNotificationEnumReference + invoke-static { v$enumMapRegister, v$freeRegister }, $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..41889cb905 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,122 +1,128 @@ package app.revanced.patches.youtube.misc.playercontrols -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.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType 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.ClassDef -internal fun indexOfFocusableInTouchModeInstruction(method: Method) = - method.indexOfFirstInstruction { - getReference()?.name == "setFocusableInTouchMode" - } - -internal fun indexOfTranslationInstruction(method: Method) = - method.indexOfFirstInstructionReversed { - getReference()?.name == "setTranslationY" - } - -internal val playerControlsVisibilityEntityModelFingerprint = fingerprint { +internal val BytecodePatchContext.playerControlsVisibilityEntityModelMethodMatch by composingFirstMethod { + name("getPlayerControlsVisibility") accessFlags(AccessFlags.PUBLIC) - returns("L") - parameters() + returnType("L") + parameterTypes() opcodes( Opcode.IGET, - Opcode.INVOKE_STATIC + Opcode.INVOKE_STATIC, ) - custom { method, _ -> - method.name == "getPlayerControlsVisibility" - } } -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) - - } +internal val BytecodePatchContext.youtubeControlsOverlayMethod by gettingFirstImmutableMethodDeclaratively { + returnType("V") + parameterTypes() + instructions( + method("setFocusableInTouchMode"), + ResourceType.ID("inset_overlay_view_layout"), + ResourceType.ID("scrim_overlay"), + ) } -internal val motionEventFingerprint = fingerprint { - returns("V") - parameters("Landroid/view/MotionEvent;") - custom { method, _ -> - indexOfTranslationInstruction(method) >= 0 - } +internal val ClassDef.motionEventMethodMatch by ClassDefComposing.composingFirstMethod { + returnType("V") + parameterTypes("Landroid/view/MotionEvent;") + instructions(method("setTranslationY")) } -internal val playerTopControlsInflateFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - literal { controls_layout_stub_id } -} - -internal val playerControlsExtensionHookListenersExistFingerprint = fingerprint { +internal val BytecodePatchContext.playerControlsExtensionHookListenersExistMethod by gettingFirstMethodDeclaratively { + name("fullscreenButtonVisibilityCallbacksExist") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("Z") - parameters() - custom { methodDef, classDef -> - methodDef.name == "fullscreenButtonVisibilityCallbacksExist" && - classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("Z") + parameterTypes() } -internal val playerControlsExtensionHookFingerprint = fingerprint { +internal val BytecodePatchContext.playerControlsExtensionHookMethod by gettingFirstMethodDeclaratively { + name("fullscreenButtonVisibilityChanged") + definingClass(EXTENSION_CLASS_DESCRIPTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) - returns("V") - parameters("Z") - custom { methodDef, classDef -> - methodDef.name == "fullscreenButtonVisibilityChanged" && - classDef.type == EXTENSION_CLASS_DESCRIPTOR - } + returnType("V") + parameterTypes("Z") } -internal val playerBottomControlsInflateFingerprint = fingerprint { - returns("Ljava/lang/Object;") - parameters() - literal { bottom_ui_container_stub_id } -} - -internal val overlayViewInflateFingerprint = fingerprint { +internal val BytecodePatchContext.playerTopControlsInflateMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Landroid/view/View;") - custom { methodDef, _ -> - methodDef.containsLiteralInstruction(fullscreen_button_id) && - methodDef.containsLiteralInstruction(heatseeker_viewstub_id) - } + returnType("V") + parameterTypes() + instructions( + ResourceType.ID("controls_layout_stub"), + method { name == "inflate" && definingClass == "Landroid/view/ViewStub;" }, + after(Opcode.MOVE_RESULT_OBJECT()), + ) +} + +internal val BytecodePatchContext.playerBottomControlsInflateMethodMatch by composingFirstMethod { + returnType("Ljava/lang/Object;") + parameterTypes() + instructions( + ResourceType.ID("bottom_ui_container_stub"), + method { name == "inflate" && definingClass == "Landroid/view/ViewStub;" }, + after(Opcode.MOVE_RESULT_OBJECT()), + ) +} + +internal val BytecodePatchContext.overlayViewInflateMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("Landroid/view/View;") + instructions( + ResourceType.ID("heatseeker_viewstub"), + ResourceType.ID("fullscreen_button"), + allOf(Opcode.CHECK_CAST(), type("Landroid/widget/ImageView;")), + ) } /** - * Resolves to the class found in [playerTopControlsInflateFingerprint]. + * Resolves to the class found in [playerTopControlsInflateMethodMatch]. */ -internal val controlsOverlayVisibilityFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getControlsOverlayVisibilityMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) - returns("V") - parameters("Z", "Z") + returnType("V") + parameterTypes("Z", "Z") } -internal val playerBottomControlsExploderFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.playerBottomControlsExploderFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters() - literal { 45643739L } + returnType("Z") + parameterTypes() + instructions(45643739L()) } -internal val playerTopControlsExperimentalLayoutFeatureFlagFingerprint = fingerprint { +internal val BytecodePatchContext.playerTopControlsExperimentalLayoutFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("I") - parameters() - literal { 45629424L } + returnType("I") + parameterTypes() + instructions(45629424L()) } +internal val BytecodePatchContext.playerControlsLargeOverlayButtonsFeatureFlagMethod by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Z") + parameterTypes() + instructions(45709810L()) +} + +internal val BytecodePatchContext.playerControlsFullscreenLargeButtonsFeatureFlagMethod by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Z") + parameterTypes() + instructions(45686474L()) +} + +internal val BytecodePatchContext.playerControlsButtonStrokeFeatureFlagMethod by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Z") + parameterTypes() + instructions(45713296L()) +} 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..aff5b9a4db 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 @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.misc.playercontrols -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.util.indexOfFirstInstructionOrThrow @@ -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 { + apply { + playerControlsVisibilityEntityModelMethodMatch.let { it.method.apply { - val startIndex = it.patternMatch!!.startIndex + val startIndex = it[0] val iGetReference = getInstruction(startIndex).reference val staticReference = getInstruction(startIndex + 1).reference @@ -33,7 +33,7 @@ val PlayerControlsOverlayVisibilityPatch = bytecodePatch { invoke-static { v$targetRegister }, $staticReference move-result-object v$targetRegister invoke-static { v$targetRegister }, $EXTENSION_PLAYER_CONTROLS_VISIBILITY_HOOK_CLASS_DESCRIPTOR->setPlayerControlsVisibility(Ljava/lang/Enum;)V - """ + """, ) } } 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..973fa94ff5 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 @@ -1,23 +1,19 @@ package app.revanced.patches.youtube.misc.playercontrols -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.PatchException 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.patches.youtube.misc.playservice.* import app.revanced.util.* 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 +35,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. */ @@ -65,16 +46,9 @@ val playerControlsResourcePatch = resourcePatch { lateinit var bottomTargetDocument: Document - execute { + apply { 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 +61,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( @@ -145,7 +131,7 @@ val playerControlsResourcePatch = resourcePatch { } } - finalize { + afterDependents { val childNodes = bottomTargetDocument.childNodes arrayOf( @@ -189,16 +175,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 +202,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 visibilityImmediateCallbacksExistMethod: MutableMethod +private var visibilityImmediateCallbacksExistModified = false private lateinit var visibilityMethod: MutableMethod -private var visibilityInsertIndex: Int = 0 - -private var visibilityImmediateCallbacksExistModified = false -private lateinit var visibilityImmediateCallbacksExistMethod : 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,46 +227,35 @@ val playerControlsPatch = bytecodePatch( dependsOn( playerControlsResourcePatch, sharedExtensionPatch, - PlayerControlsOverlayVisibilityPatch + resourceMappingPatch, // Used to find methods. + playerControlsOverlayVisibilityPatch, + versionCheckPatch, ) - execute { - fun MutableMethod.indexOfFirstViewInflateOrThrow() = indexOfFirstInstructionOrThrow { - val reference = getReference() - reference?.definingClass == "Landroid/view/ViewStub;" && - reference.name == "inflate" - } - - playerBottomControlsInflateFingerprint.method.apply { + apply { + playerBottomControlsInflateMethodMatch.method.apply { inflateBottomControlMethod = this - val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1 + val inflateReturnObjectIndex = playerBottomControlsInflateMethodMatch[-1] inflateBottomControlRegister = getInstruction(inflateReturnObjectIndex).registerA inflateBottomControlInsertIndex = inflateReturnObjectIndex + 1 } - playerTopControlsInflateFingerprint.method.apply { + playerTopControlsInflateMethodMatch.method.apply { inflateTopControlMethod = this - val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1 + val inflateReturnObjectIndex = playerTopControlsInflateMethodMatch[-1] inflateTopControlRegister = getInstruction(inflateReturnObjectIndex).registerA inflateTopControlInsertIndex = inflateReturnObjectIndex + 1 } - visibilityMethod = controlsOverlayVisibilityFingerprint.match( - playerTopControlsInflateFingerprint.originalClassDef, - ).method + visibilityMethod = + playerTopControlsInflateMethodMatch.immutableClassDef.getControlsOverlayVisibilityMethod() // Hook the fullscreen close button. Used to fix visibility // when seeking and other situations. - overlayViewInflateFingerprint.method.apply { - val resourceIndex = indexOfFirstLiteralInstructionReversedOrThrow(fullscreen_button_id) - - val index = indexOfFirstInstructionOrThrow(resourceIndex) { - opcode == Opcode.CHECK_CAST && - getReference()?.type == - "Landroid/widget/ImageView;" - } + overlayViewInflateMethodMatch.method.apply { + val index = overlayViewInflateMethodMatch[-1] val register = getInstruction(index).registerA addInstruction( @@ -290,12 +265,12 @@ val playerControlsPatch = bytecodePatch( ) } - visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistFingerprint.method - visibilityImmediateMethod = playerControlsExtensionHookFingerprint.method + visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistMethod + visibilityImmediateMethod = playerControlsExtensionHookMethod - motionEventFingerprint.match(youtubeControlsOverlayFingerprint.originalClassDef).method.apply { - visibilityNegatedImmediateMethod = this - visibilityNegatedImmediateInsertIndex = indexOfTranslationInstruction(this) + 1 + youtubeControlsOverlayMethod.immutableClassDef.motionEventMethodMatch.let { + visibilityNegatedImmediateMethod = it.method + visibilityNegatedImmediateInsertIndex = it[0] + 1 } // A/B test for a slightly different bottom overlay controls, @@ -303,27 +278,33 @@ val playerControlsPatch = bytecodePatch( // The change to support this is simple and only requires adding buttons to both layout files, // but for now force this different layout off since it's still an experimental test. if (is_19_35_or_greater) { - playerBottomControlsExploderFeatureFlagFingerprint.method.returnLate(false) + playerBottomControlsExploderFeatureFlagMethod.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) { - playerTopControlsExperimentalLayoutFeatureFlagFingerprint.method.apply { + // Flag was removed in 20.19+ + if (is_19_25_or_greater && !is_20_19_or_greater) { + playerTopControlsExperimentalLayoutFeatureFlagMethod.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) { + playerControlsFullscreenLargeButtonsFeatureFlagMethod.returnLate(false) + + if (is_20_28_or_greater) { + playerControlsLargeOverlayButtonsFeatureFlagMethod.returnLate(false) + } + + if (is_20_30_or_greater) { + playerControlsButtonStrokeFeatureFlagMethod.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..d182b41500 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,55 @@ package app.revanced.patches.youtube.misc.playertype -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType 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, - ) - custom { _, classDef -> classDef.endsWith("/YouTubePlayerOverlaysLayout;") } +internal val BytecodePatchContext.playerTypeEnumMethod by gettingFirstImmutableMethodDeclaratively( + "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", +) { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) } -internal val reelWatchPagerFingerprint = fingerprint { +internal val BytecodePatchContext.reelWatchPagerMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Landroid/view/View;") - literal { reelWatchPlayerId } -} - -internal val videoStateFingerprint = 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 + returnType("Landroid/view/View;") + instructions( + ResourceType.ID("reel_watch_player"), + afterAtMost(10, Opcode.MOVE_RESULT_OBJECT()), + ) +} + +internal val BytecodePatchContext.videoStateEnumMethod by gettingFirstImmutableMethodDeclaratively( + "NEW", + "PLAYING", + "PAUSED", + "RECOVERABLE_ERROR", + "UNRECOVERABLE_ERROR", + "ENDED", +) { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + parameterTypes() +} + +// 20.33 and lower class name ControlsState. 20.34+ class name is obfuscated. +internal val BytecodePatchContext.controlsStateToStringMethod by gettingFirstImmutableMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameterTypes() + returnType("Ljava/lang/String;") + instructions( + "videoState"(), + "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..910c14fab0 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 @@ -1,66 +1,72 @@ 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.* +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction 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.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( + apply { + firstMethodDeclaratively { + definingClass("/YouTubePlayerOverlaysLayout;") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes(playerTypeEnumMethod.immutableClassDef.type) + }.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 + reelWatchPagerMethodMatch.method.apply { + val index = reelWatchPagerMethodMatch[-1] + val register = getInstruction(index).registerA addInstruction( - registerIndex + 1, - "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR->onShortsCreate(Landroid/view/View;)V" + 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 = controlsStateToStringMethod.immutableClassDef.type - addInstructions( - 0, - """ - iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field - invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->setVideoState(Ljava/lang/Enum;)V - """ + val videoStateEnumMethod = videoStateEnumMethod + firstMethodComposite { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes(controlStateType) + instructions( + field { + definingClass == controlStateType && type == videoStateEnumMethod.immutableClassDef.type + }, + // Obfuscated parameter field name. + ResourceType.STRING("accessibility_play"), + ResourceType.STRING("accessibility_pause"), ) + }.let { + it.method.apply { + val videoStateFieldName = getInstruction(it[0]).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..aa761dac94 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,19 +69,44 @@ 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.", ) { - execute { + apply { // The app version is missing from the decompiled manifest, // so instead use the Google Play services version and compare against specific releases. 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..efece581f2 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 @@ -1,12 +1,14 @@ package app.revanced.patches.youtube.misc.recyclerviewtree.hook -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.opcodes +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val recyclerViewTreeObserverFingerprint = fingerprint { +internal val BytecodePatchContext.recyclerViewTreeObserverMethodMatch by composingFirstMethod("LithoRVSLCBinder") { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") opcodes( Opcode.CHECK_CAST, Opcode.NEW_INSTANCE, @@ -14,5 +16,4 @@ internal val recyclerViewTreeObserverFingerprint = fingerprint { Opcode.INVOKE_VIRTUAL, Opcode.NEW_INSTANCE, ) - strings("LithoRVSLCBinder") } 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..70182237f6 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 @@ -1,6 +1,6 @@ package app.revanced.patches.youtube.misc.recyclerviewtree.hook -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch @@ -10,13 +10,13 @@ lateinit var addRecyclerViewTreeHook: (String) -> Unit val recyclerViewTreeHookPatch = bytecodePatch { dependsOn(sharedExtensionPatch) - execute { - recyclerViewTreeObserverFingerprint.method.apply { - val insertIndex = recyclerViewTreeObserverFingerprint.patternMatch!!.startIndex + 1 + apply { + recyclerViewTreeObserverMethodMatch.let { + val insertIndex = it[0] + 1 val recyclerViewParameter = 2 addRecyclerViewTreeHook = { classDescriptor -> - addInstruction( + it.method.addInstruction( insertIndex, "invoke-static/range { p$recyclerViewParameter .. p$recyclerViewParameter }, " + "$classDescriptor->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V", 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..37b3b50806 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,29 +1,42 @@ package app.revanced.patches.youtube.misc.settings -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode -internal val licenseActivityOnCreateFingerprint = fingerprint { +internal val BytecodePatchContext.licenseActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass("/LicenseActivity;") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - custom { method, classDef -> - classDef.endsWith("LicenseActivity;") && method.name == "onCreate" - } + returnType("V") + parameterTypes("Landroid/os/Bundle;") } -internal val setThemeFingerprint = fingerprint { +internal val BytecodePatchContext.setThemeMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters() - literal { appearanceStringId } + returnType("L") + parameterTypes() + instructions(ResourceType.STRING("app_theme_appearance_dark")) } -internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L - -internal val cairoFragmentConfigFingerprint = fingerprint { +internal val BytecodePatchContext.cairoFragmentConfigMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - literal { CAIRO_CONFIG_LITERAL_VALUE } + returnType("Z") + instructions( + 45532100L(), + afterAtMost(10, Opcode.MOVE_RESULT()), + ) +} + +// 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 BytecodePatchContext.boldIconsFeatureFlagMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("Z") + parameterTypes() + instructions( + 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..38b200d4b3 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 @@ -1,26 +1,27 @@ package app.revanced.patches.youtube.misc.settings -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableClassDef +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.resourcePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable 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.PreferenceScreenPreference.Sorting 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 com.android.tools.smali.dexlib2.AccessFlags @@ -36,21 +37,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"), ) to "settings_fragment", + PreferenceCategory( titleKey = "revanced_settings_title", layout = "@layout/preference_group_title", @@ -58,41 +57,55 @@ 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"), - ) - ) + ), + ), ) to "settings_fragment_cairo", ), - preferences - ) + preferences = preferences, + ), ) - execute { - appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"] - + apply { // Use same colors as stock YouTube. overrideThemeColors("@color/yt_white1", "@color/yt_black3") copyResources( "settings", - ResourceGroup("drawable", + 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", + ), ) // Remove horizontal divider from the settings Preferences @@ -130,7 +143,7 @@ private val settingsResourcePatch = resourcePatch { licenseElement.setAttribute( "android:configChanges", - "orientation|screenSize|keyboardHidden" + "orientation|screenSize|keyboardHidden", ) val mimeType = document.createElement("data") @@ -153,18 +166,20 @@ 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, ) - execute { + apply { addResources("youtube", "misc.settings.settingsPatch") // Add an "about" preference to the top. 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", @@ -173,13 +188,28 @@ val settingsPatch = bytecodePatch( if (is_19_34_or_greater) { PreferenceScreen.GENERAL_LAYOUT.addPreferences( - SwitchPreference("revanced_restore_old_settings_menus") + SwitchPreference("revanced_restore_old_settings_menus"), ) } 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( @@ -192,14 +222,14 @@ val settingsPatch = bytecodePatch( ), ListPreference( key = "revanced_language", - tag = "app.revanced.extension.shared.settings.preference.SortedListPreference" - ) + tag = "app.revanced.extension.shared.settings.preference.SortedListPreference", + ), ) // Update shared dark mode status based on YT theme. // This is needed because YT allows forcing light/dark mode // which then differs from the system dark mode status. - setThemeFingerprint.method.apply { + setThemeMethod.apply { findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index -> val register = getInstruction(index).registerA addInstructionsAtControlFlowLabel( @@ -210,20 +240,31 @@ val settingsPatch = bytecodePatch( } // Add setting to force Cairo settings fragment on/off. - cairoFragmentConfigFingerprint.method.insertLiteralOverride( - CAIRO_CONFIG_LITERAL_VALUE, - "$YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR->useCairoSettingsFragment(Z)Z" + cairoFragmentConfigMethodMatch.method.insertLiteralOverride( + cairoFragmentConfigMethodMatch[0], + "$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) { + boldIconsFeatureFlagMethodMatch.let { + it.method.insertLiteralOverride( + it[0], + "$YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR->useBoldIcons(Z)Z", + ) + } + } + modifyActivityForSettingsInjection( - licenseActivityOnCreateFingerprint.classDef, - licenseActivityOnCreateFingerprint.method, + licenseActivityOnCreateMethod.classDef, + licenseActivityOnCreateMethod, YOUTUBE_ACTIVITY_HOOK_CLASS_DESCRIPTOR, - false + false, ) } - finalize { + afterDependents { PreferenceScreen.close() } } @@ -232,20 +273,21 @@ val settingsPatch = bytecodePatch( * Modifies the activity to show ReVanced settings instead of it's original purpose. */ internal fun modifyActivityForSettingsInjection( - activityOnCreateClass: MutableClass, + activityOnCreateClass: MutableClassDef, activityOnCreateMethod: MutableMethod, extensionClassType: String, - isYouTubeMusic: Boolean + isYouTubeMusic: Boolean, ) { // Modify Activity and remove all existing layout code. // 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 - """ + """, ) // Remove other methods as they will break as the onCreate method is modified above. @@ -270,7 +312,7 @@ internal fun modifyActivityForSettingsInjection( move-result-object p1 invoke-super { p0, p1 }, ${activityOnCreateClass.superclass}->attachBaseContext(Landroid/content/Context;)V return-void - """ + """, ) }.let(activityOnCreateClass.methods::add) @@ -297,7 +339,7 @@ internal fun modifyActivityForSettingsInjection( $invokeFinishOpcode { p0 }, Landroid/app/Activity;->finish()V :search_handled return-void - """ + """, ) }.let(activityOnCreateClass.methods::add) } @@ -321,12 +363,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 +378,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 +421,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 +429,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 +437,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..428d55af73 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 @@ -1,5 +1,6 @@ package app.revanced.patches.youtube.misc.spoof +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference @@ -13,11 +14,11 @@ 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.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod val spoofVideoStreamsPatch = spoofVideoStreamsPatch( extensionClassDescriptor = "Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;", - mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + getMainActivityOnCreateMethod = BytecodePatchContext::mainActivityOnCreateMethod::get, fixMediaFetchHotConfig = { is_19_34_or_greater }, @@ -32,17 +33,17 @@ 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", + ), ) dependsOn( userAgentClientSpoofPatch, settingsPatch, - versionCheckPatch + versionCheckPatch, ) }, @@ -60,12 +61,12 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( // Requires a key and title but the actual text is chosen at runtime. key = "revanced_spoof_video_streams_about", summaryKey = null, - tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference" + tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference", ), SwitchPreference("revanced_spoof_video_streams_av1"), SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"), - ) - ) + ), + ), ) - } + }, ) 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..0397f26992 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,83 +1,88 @@ package app.revanced.patches.youtube.shared -import app.revanced.patcher.fingerprint +import app.revanced.patcher.ClassDefComposing +import app.revanced.patcher.accessFlags +import app.revanced.patcher.after +import app.revanced.patcher.allOf +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.custom +import app.revanced.patcher.definingClass +import app.revanced.patcher.field +import app.revanced.patcher.firstMethodComposite +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.immutableClassDef +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.method +import app.revanced.patcher.name +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType +import app.revanced.patcher.type +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef internal const val YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE = "Lcom/google/android/apps/youtube/app/watchwhile/MainActivity;" -internal val conversionContextFingerprintToString = fingerprint { - parameters() - strings( - "ConversionContext{containerInternal=", - ", widthConstraint=", - ", heightConstraint=", - ", templateLoggerFactory=", - ", rootDisposableContainer=", - ", identifierProperty=" - ) - custom { method, _ -> - method.name == "toString" - } +internal val BytecodePatchContext.conversionContextToStringMethod by gettingFirstImmutableMethodDeclaratively( + ", widthConstraint=", + ", heightConstraint=", + ", templateLoggerFactory=", + ", rootDisposableContainer=", + ", identifierProperty=", +) { + name("toString") + parameterTypes() + instructions("ConversionContext{"(String::startsWith)) // Partial string match. } -/** - * Resolves to class found in [loopVideoParentFingerprint]. - */ -internal val loopVideoFingerprint = fingerprint { +internal fun BytecodePatchContext.getLayoutConstructorMethodMatch() = firstMethodComposite { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - custom { method, _ -> - method.implementation!!.instructions.count() == 3 && method.annotations.isEmpty() - } -} + returnType("V") + parameterTypes() -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", + val methodParameterTypePrefixes = listOf("Landroid/view/View;", "I") + + instructions( + 159962L(), + ResourceType.ID("player_control_previous_button_touch_area"), + ResourceType.ID("player_control_next_button_touch_area"), + method { + parameterTypes.size == 2 && + parameterTypes.zip(methodParameterTypePrefixes).all { (a, b) -> a.startsWith(b) } + }, ) } -internal val layoutConstructorFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - strings("1.0x") -} - -internal val mainActivityConstructorFingerprint = fingerprint { +internal val BytecodePatchContext.mainActivityConstructorMethod by gettingFirstImmutableMethodDeclaratively { + definingClass(YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE) accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters() - custom { _, classDef -> - classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE - } + parameterTypes() } -internal val mainActivityOnBackPressedFingerprint = fingerprint { +internal val BytecodePatchContext.mainActivityOnBackPressedMethod by gettingFirstMethodDeclaratively { + name("onBackPressed") + definingClass(YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters() - custom { method, classDef -> - method.name == "onBackPressed" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE - } + returnType("V") + parameterTypes() } -internal val mainActivityOnCreateFingerprint = fingerprint { - returns("V") - parameters("Landroid/os/Bundle;") - custom { method, classDef -> - method.name == "onCreate" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE - } +internal val BytecodePatchContext.mainActivityOnCreateMethod by gettingFirstMethodDeclaratively { + name("onCreate") + definingClass(YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE) + returnType("V") + parameterTypes("Landroid/os/Bundle;") } -internal val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint { +internal val BytecodePatchContext.rollingNumberTextViewAnimationUpdateMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("Landroid/graphics/Bitmap;") + returnType("V") + parameterTypes("Landroid/graphics/Bitmap;") opcodes( Opcode.NEW_INSTANCE, // bitmap ImageSpan Opcode.INVOKE_VIRTUAL, @@ -93,61 +98,47 @@ internal val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint { Opcode.INT_TO_FLOAT, Opcode.INVOKE_VIRTUAL, // set textview padding using bitmap width ) - custom { _, classDef -> - classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" || - classDef.superclass == + custom { + immutableClassDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" || + immutableClassDef.superclass == "Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;" } } -internal val seekbarFingerprint = fingerprint { - returns("V") - strings("timed_markers_width") +internal val BytecodePatchContext.seekbarMethod by gettingFirstImmutableMethodDeclaratively { + returnType("V") + instructions("timed_markers_width"()) } -internal val seekbarOnDrawFingerprint = fingerprint { - custom { method, _ -> method.name == "onDraw" } -} - -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, +/** + * Matches to _mutable_ class found in [seekbarMethod]. + */ +internal fun ClassDef.getSeekbarOnDrawMethodMatch() = firstMethodComposite { + name("onDraw") + instructions( + method { toString() == "Ljava/lang/Math;->round(F)I" }, + after(Opcode.MOVE_RESULT()), ) } -internal val videoQualityChangedFingerprint = fingerprint { +internal val BytecodePatchContext.subtitleButtonControllerMethod by gettingFirstMethodDeclaratively { 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, + returnType("V") + parameterTypes("Lcom/google/android/libraries/youtube/player/subtitles/model/SubtitleTrack;") + instructions( + ResourceType.STRING("accessibility_captions_unavailable"), + ResourceType.STRING("accessibility_captions_button_name"), + ) +} + +internal val BytecodePatchContext.videoQualityChangedMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("L") + parameterTypes("L") + instructions( + allOf(Opcode.NEW_INSTANCE(), type("Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;")), + Opcode.IGET_OBJECT(), + Opcode.CHECK_CAST(), + after(allOf(Opcode.IGET(), field { type == "I" })), // 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..90fbbe740f 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 @@ -1,12 +1,13 @@ package app.revanced.patches.youtube.video.audio +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patches.shared.misc.audio.forceOriginalAudioPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_20_07_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.mainActivityOnCreateFingerprint +import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod @Suppress("unused") val forceOriginalAudioPatch = forceOriginalAudioPatch( @@ -14,20 +15,20 @@ val forceOriginalAudioPatch = forceOriginalAudioPatch( dependsOn( sharedExtensionPatch, settingsPatch, - versionCheckPatch + 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", + ), ) }, fixUseLocalizedAudioTrackFlag = { is_20_07_or_greater }, - mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + getMainActivityOnCreateMethod = BytecodePatchContext::mainActivityOnCreateMethod::get, subclassExtensionClassDescriptor = "Lapp/revanced/extension/youtube/patches/ForceOriginalAudioPatch;", preferenceScreen = PreferenceScreen.VIDEO, ) 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..a7f027fd73 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 @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.video.codecs -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -14,8 +14,7 @@ import app.revanced.util.getReference 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/youtube/patches/DisableVideoCodecsPatch;" +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisableVideoCodecsPatch;" @Suppress("unused") val disableVideoCodecsPatch = bytecodePatch( @@ -36,8 +35,7 @@ val disableVideoCodecsPatch = bytecodePatch( } val reference = instruction.getReference() - if (reference?.definingClass =="Landroid/view/Display\$HdrCapabilities;" - && reference.name == "getSupportedHdrTypes") { + if (reference?.definingClass == "Landroid/view/Display\$HdrCapabilities;" && reference.name == "getSupportedHdrTypes") { return@filterMap instruction to instructionIndex } return@filterMap null @@ -49,30 +47,30 @@ val disableVideoCodecsPatch = bytecodePatch( method.replaceInstruction( index, "invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->" + - "disableHdrVideo(Landroid/view/Display\$HdrCapabilities;)[I", + $$"disableHdrVideo(Landroid/view/Display$HdrCapabilities;)[I", ) - } - ) + }, + ), ) 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 { + apply { addResources("youtube", "video.codecs.disableVideoCodecsPatch") PreferenceScreen.VIDEO.addPreferences( SwitchPreference("revanced_disable_hdr_video"), - SwitchPreference("revanced_force_avc_codec") + SwitchPreference("revanced_force_avc_codec"), ) - vp9CapabilityFingerprint.method.addInstructionsWithLabels( + vp9CapabilityMethod.addInstructionsWithLabels( 0, """ invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->allowVP9()Z @@ -81,7 +79,7 @@ val disableVideoCodecsPatch = bytecodePatch( return v0 :default nop - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/Fingerprints.kt index a7790191f4..613d3c623b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/codecs/Fingerprints.kt @@ -1,13 +1,16 @@ package app.revanced.patches.youtube.video.codecs -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.gettingFirstMethodDeclaratively +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType import com.android.tools.smali.dexlib2.AccessFlags -internal val vp9CapabilityFingerprint = fingerprint { +internal val BytecodePatchContext.vp9CapabilityMethod by gettingFirstMethodDeclaratively( + "vp9_supported", + "video/x-vnd.on2.vp9", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - strings( - "vp9_supported", - "video/x-vnd.on2.vp9" - ) + returnType("Z") } 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..9f3b8e52cb 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,47 +1,52 @@ package app.revanced.patches.youtube.video.information -import app.revanced.patcher.fingerprint -import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint -import app.revanced.util.getReference +import app.revanced.patcher.* +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.youtube.shared.videoQualityChangedMethodMatch import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.ClassDef +import org.stringtemplate.v4.compiler.Bytecode -internal val createVideoPlayerSeekbarFingerprint = fingerprint { - returns("V") - strings("timed_markers_width") +internal val BytecodePatchContext.createVideoPlayerSeekbarMethod by gettingFirstImmutableMethodDeclaratively { + returnType("V") + instructions("timed_markers_width"()) } -internal val onPlaybackSpeedItemClickFingerprint = fingerprint { +internal val BytecodePatchContext.onPlaybackSpeedItemClickMethod by gettingFirstMethodDeclaratively { + name("onItemClick") accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L", "L", "I", "J") - custom { method, _ -> - method.name == "onItemClick" && - method.implementation?.instructions?.find { - it.opcode == Opcode.IGET_OBJECT && - it.getReference()!!.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" - } != null + returnType("V") + parameterTypes("L", "L", "I", "J") + instructions( + allOf( + Opcode.IGET_OBJECT(), + field { type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" }, + ), + ) +} + +internal val BytecodePatchContext.playerControllerSetTimeReferenceMethodMatch by + composingFirstMethod("Media progress reported outside media playback: ") { + opcodes( + Opcode.INVOKE_DIRECT_RANGE, + Opcode.IGET_OBJECT, + ) } -} -internal val playerControllerSetTimeReferenceFingerprint = fingerprint { - opcodes(Opcode.INVOKE_DIRECT_RANGE, Opcode.IGET_OBJECT) - strings("Media progress reported outside media playback: ") -} - -internal val playerInitFingerprint = fingerprint { - strings("playVideo called on player response with no videoStreamingData.") +internal val BytecodePatchContext.playVideoCheckVideoStreamingDataResponseMethod by gettingFirstImmutableMethodDeclaratively { + instructions("playVideo called on player response with no videoStreamingData."()) } /** - * Matched using class found in [playerInitFingerprint]. + * Matched using class found in [playVideoCheckVideoStreamingDataResponseMethod]. */ -internal val seekFingerprint = fingerprint { - strings("Attempting to seek during an ad") +internal fun ClassDef.getSeekMethod() = firstImmutableMethodDeclaratively { + instructions("Attempting to seek during an ad"()) } -internal val videoLengthFingerprint = fingerprint { +internal val ClassDef.videoLengthMethodMatch by ClassDefComposing.composingFirstMethod { opcodes( Opcode.MOVE_RESULT_WIDE, Opcode.CMP_LONG, @@ -59,101 +64,123 @@ internal val videoLengthFingerprint = fingerprint { } /** - * Matches using class found in [mdxPlayerDirectorSetVideoStageFingerprint]. + * Matches using class found in [mdxPlayerDirectorSetVideoStageMethod]. */ -internal val mdxSeekFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getMdxSeekMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("Z") - parameters("J", "L") + returnType("Z") + parameterTypes("J", "L") opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, Opcode.RETURN, ) - custom { methodDef, _ -> + custom { // The instruction count is necessary here to avoid matching the relative version // of the seek method we're after, which has the same function signature as the // regular one, is in the same class, and even has the exact same 3 opcodes pattern. - methodDef.implementation!!.instructions.count() == 3 + instructions.count() == 3 } } -internal val mdxPlayerDirectorSetVideoStageFingerprint = fingerprint { - strings("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state ") +internal val BytecodePatchContext.mdxPlayerDirectorSetVideoStageMethod by gettingFirstImmutableMethodDeclaratively { + instructions("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state "()) } /** - * Matches using class found in [mdxPlayerDirectorSetVideoStageFingerprint]. + * Matches using class found in [mdxPlayerDirectorSetVideoStageMethod]. */ -internal val mdxSeekRelativeFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getMdxSeekRelativeMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) // Return type is boolean up to 19.39, and void with 19.39+. - parameters("J", "L") + parameterTypes("J", "L") opcodes( - Opcode.IGET_OBJECT, Opcode.INVOKE_INTERFACE, ) } /** - * Matches using class found in [playerInitFingerprint]. + * Matches using class found in [playVideoCheckVideoStreamingDataResponseMethod]. */ -internal val seekRelativeFingerprint = fingerprint { +context(_: BytecodePatchContext) +internal fun ClassDef.getSeekRelativeMethod() = firstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) // Return type is boolean up to 19.39, and void with 19.39+. - parameters("J", "L") + parameterTypes("J", "L") opcodes( Opcode.ADD_LONG_2ADDR, Opcode.INVOKE_VIRTUAL, ) } -/** - * Resolves with the class found in [videoQualityChangedFingerprint]. - */ -internal val playbackSpeedMenuSpeedChangedFingerprint = fingerprint { +internal val BytecodePatchContext.videoEndMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters("L") - opcodes( - Opcode.IGET, - Opcode.INVOKE_VIRTUAL, - Opcode.SGET_OBJECT, - Opcode.RETURN_OBJECT, + returnType("Z") + parameterTypes("J", "L") + instructions( + method { parameterTypes.isEmpty() && returnType == "V" }, + afterAtMost(5, 45368273L()), + "Attempting to seek when video is not playing"(), ) } -internal val playbackSpeedClassFingerprint = fingerprint { +/** + * Matches with the class found in [videoQualityChangedMethodMatch]. + */ +internal val ClassDef.playbackSpeedMenuSpeedChangedMethodMatch by ClassDefComposing.composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("L") + parameterTypes("L") + instructions(allOf(Opcode.IGET(), field { type == "F" })) +} + +internal val BytecodePatchContext.playbackSpeedClassMethod by gettingFirstMethodDeclaratively( + "PLAYBACK_RATE_MENU_BOTTOM_SHEET_FRAGMENT", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("L") - parameters("L") - opcodes( - Opcode.RETURN_OBJECT - ) - strings("PLAYBACK_RATE_MENU_BOTTOM_SHEET_FRAGMENT") + returnType("L") + parameterTypes("L") + opcodes(Opcode.RETURN_OBJECT) } +internal const val YOUTUBE_VIDEO_QUALITY_CLASS_TYPE = + "Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;" -internal const val YOUTUBE_VIDEO_QUALITY_CLASS_TYPE = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;" - -internal val videoQualityFingerprint = fingerprint { +/** + * YouTube 20.19 and lower. + */ +internal val BytecodePatchContext.videoQualityLegacyMethod by gettingFirstMethodDeclaratively { + definingClass(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE) accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameters( + parameterTypes( "I", // Resolution. "Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc "Z", - "L" + "L", ) - custom { _, classDef -> - classDef.type == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE - } } -internal val videoQualitySetterFingerprint = fingerprint { +internal val BytecodePatchContext.videoQualityMethod by gettingFirstMethodDeclaratively { + definingClass(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE) + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameterTypes( + "I", // Resolution. + "L", + "Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc + "Z", + "L", + ) +} + +internal val BytecodePatchContext.videoQualitySetterMethod by gettingFirstMethodDeclaratively( + "menu_item_video_quality", +) { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("[L", "I", "Z") + returnType("V") + parameterTypes("[L", "I", "Z") opcodes( Opcode.IF_EQZ, Opcode.INVOKE_VIRTUAL, @@ -161,15 +188,15 @@ internal val videoQualitySetterFingerprint = fingerprint { Opcode.INVOKE_VIRTUAL, Opcode.IPUT_BOOLEAN, ) - strings("menu_item_video_quality") } /** - * Matches with the class found in [videoQualitySetterFingerprint]. + * Matches with the class found in [videoQualitySetterMethod]. */ -internal val setVideoQualityFingerprint = fingerprint { - returns("V") - parameters("L") +context(_: BytecodePatchContext) +internal fun ClassDef.getSetVideoQualityMethod() = firstMethodDeclaratively { + returnType("V") + parameterTypes("L") opcodes( Opcode.IGET_OBJECT, Opcode.IPUT_OBJECT, 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..b8bd0a4f57 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 @@ -1,15 +1,18 @@ package app.revanced.patches.youtube.video.information -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableClassDef +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.* +import app.revanced.patcher.firstClassDef +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -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.shared.videoQualityChangedFingerprint +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.videoQualityChangedMethodMatch import app.revanced.patches.youtube.video.playerresponse.Hook import app.revanced.patches.youtube.video.playerresponse.addPlayerResponseMethodHook import app.revanced.patches.youtube.video.playerresponse.playerResponseMethodHookPatch @@ -36,7 +39,8 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.util.MethodUtil -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/VideoInformation;" +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/VideoInformation;" private const val EXTENSION_PLAYER_INTERFACE = "Lapp/revanced/extension/youtube/patches/VideoInformation\$PlaybackController;" private const val EXTENSION_VIDEO_QUALITY_MENU_INTERFACE = @@ -67,6 +71,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,63 +88,71 @@ val videoInformationPatch = bytecodePatch( sharedExtensionPatch, videoIdPatch, playerResponseMethodHookPatch, + versionCheckPatch, ) - execute { - playerInitMethod = playerInitFingerprint.classDef.methods.first { MethodUtil.isConstructor(it) } + apply { + with(playVideoCheckVideoStreamingDataResponseMethod) { + playerInitMethod = classDef.methods.first { MethodUtil.isConstructor(it) } - // Find the location of the first invoke-direct call and extract the register storing the 'this' object reference. - val initThisIndex = playerInitMethod.indexOfFirstInstructionOrThrow { - opcode == Opcode.INVOKE_DIRECT && getReference()?.name == "" + // Find the location of the first invoke-direct call + // and extract the register storing the 'this' object reference. + val initThisIndex = playerInitMethod.indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_DIRECT && getReference()?.name == "" + } + playerInitInsertRegister = + playerInitMethod.getInstruction(initThisIndex).registerC + playerInitInsertIndex = initThisIndex + 1 + + // Create extension interface methods. + addSeekInterfaceMethods( + playerInitMethod.classDef, + classDef.getSeekMethod(), + classDef.getSeekRelativeMethod(), + ) } - playerInitInsertRegister = playerInitMethod.getInstruction(initThisIndex).registerC - playerInitInsertIndex = initThisIndex + 1 - val seekFingerprintResultMethod = seekFingerprint.match(playerInitFingerprint.originalClassDef).method - val seekRelativeFingerprintResultMethod = - seekRelativeFingerprint.match(playerInitFingerprint.originalClassDef).method - - // Create extension interface methods. - addSeekInterfaceMethods( - playerInitFingerprint.classDef, - seekFingerprintResultMethod, - seekRelativeFingerprintResultMethod, - ) - - with(mdxPlayerDirectorSetVideoStageFingerprint) { + with(mdxPlayerDirectorSetVideoStageMethod) { mdxInitMethod = classDef.methods.first { MethodUtil.isConstructor(it) } val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow { opcode == Opcode.INVOKE_DIRECT && getReference()?.name == "" } - mdxInitInsertRegister = mdxInitMethod.getInstruction(initThisIndex).registerC + mdxInitInsertRegister = + mdxInitMethod.getInstruction(initThisIndex).registerC mdxInitInsertIndex = initThisIndex + 1 // Hook the MDX director for use through the extension. onCreateHookMdx(EXTENSION_CLASS_DESCRIPTOR, "initializeMdx") - val mdxSeekFingerprintResultMethod = mdxSeekFingerprint.match(classDef).method - val mdxSeekRelativeFingerprintResultMethod = mdxSeekRelativeFingerprint.match(classDef).method - - addSeekInterfaceMethods(classDef, mdxSeekFingerprintResultMethod, mdxSeekRelativeFingerprintResultMethod) + addSeekInterfaceMethods( + classDef, + classDef.getMdxSeekMethod(), + classDef.getMdxSeekRelativeMethod(), + ) } - with(createVideoPlayerSeekbarFingerprint) { - val videoLengthMethodMatch = videoLengthFingerprint.match(originalClassDef) + with(createVideoPlayerSeekbarMethod) { + val videoLengthMethodMatch = immutableClassDef.videoLengthMethodMatch videoLengthMethodMatch.method.apply { - val videoLengthRegisterIndex = videoLengthMethodMatch.patternMatch!!.endIndex - 2 - val videoLengthRegister = getInstruction(videoLengthRegisterIndex).registerA - val dummyRegisterForLong = videoLengthRegister + 1 // required for long values since they are wide + val videoLengthRegisterIndex = videoLengthMethodMatch[-1] - 2 + val videoLengthRegister = + getInstruction(videoLengthRegisterIndex).registerA + val dummyRegisterForLong = + videoLengthRegister + 1 // Required for long values since they are wide. addInstruction( - videoLengthMethodMatch.patternMatch!!.endIndex, + videoLengthMethodMatch[-1], "invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, " + - "$EXTENSION_CLASS_DESCRIPTOR->setVideoLength(J)V", + "$EXTENSION_CLASS_DESCRIPTOR->setVideoLength(J)V", ) } } + videoEndMethod = navigate(videoEndMethodMatch.immutableMethod) + .to(videoEndMethodMatch[0]).stop() + /* * Inject call for video ids */ @@ -153,16 +167,15 @@ val videoInformationPatch = bytecodePatch( addPlayerResponseMethodHook( Hook.ProtoBufferParameterBeforeVideoId( "$EXTENSION_CLASS_DESCRIPTOR->" + - "newPlayerResponseSignature(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", + "newPlayerResponseSignature(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", ), ) /* * Set the video time method */ - timeMethod = navigate(playerControllerSetTimeReferenceFingerprint.originalMethod) - .to(playerControllerSetTimeReferenceFingerprint.patternMatch!!.startIndex) - .stop() + timeMethod = navigate(playerControllerSetTimeReferenceMethodMatch.immutableMethod) + .to(playerControllerSetTimeReferenceMethodMatch[0]).stop() /* * Hook the methods which set the time @@ -172,7 +185,7 @@ val videoInformationPatch = bytecodePatch( /* * Hook the user playback speed selection. */ - onPlaybackSpeedItemClickFingerprint.method.apply { + onPlaybackSpeedItemClickMethod.apply { val speedSelectionValueInstructionIndex = indexOfFirstInstructionOrThrow(Opcode.IGET) legacySpeedSelectionInsertMethod = this @@ -188,12 +201,12 @@ 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 } + firstClassDef(setPlaybackSpeedMethodReference.definingClass) + .methods.first { it.name == setPlaybackSpeedMethodReference.name } setPlaybackSpeedMethodIndex = 0 // Add override playback speed method. - onPlaybackSpeedItemClickFingerprint.classDef.methods.add( + onPlaybackSpeedItemClickMethod.classDef.methods.add( ImmutableMethod( definingClass, "overridePlaybackSpeed", @@ -224,13 +237,15 @@ val videoInformationPatch = bytecodePatch( :ignore return-void - """.toInstructions(), null, null - ) - ).toMutable() + """.toInstructions(), + null, + null, + ), + ).toMutable(), ) } - playbackSpeedClassFingerprint.method.apply { + playbackSpeedClassMethod.apply { val index = indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) val register = getInstruction(index).registerA val playbackSpeedClass = this.returnType @@ -238,7 +253,7 @@ val videoInformationPatch = bytecodePatch( // Set playback speed class. addInstructionsAtControlFlowLabel( index, - "sput-object v$register, $EXTENSION_CLASS_DESCRIPTOR->playbackSpeedClass:$playbackSpeedClass" + "sput-object v$register, $EXTENSION_CLASS_DESCRIPTOR->playbackSpeedClass:$playbackSpeedClass", ) val smaliInstructions = @@ -255,33 +270,35 @@ val videoInformationPatch = bytecodePatch( "overridePlaybackSpeed", "playbackSpeedClass", playbackSpeedClass, - smaliInstructions + smaliInstructions, ) } // Handle new playback speed menu. - playbackSpeedMenuSpeedChangedFingerprint.match( - videoQualityChangedFingerprint.originalClassDef, - ).method.apply { - val index = indexOfFirstInstructionOrThrow(Opcode.IGET) + videoQualityChangedMethodMatch.immutableClassDef.playbackSpeedMenuSpeedChangedMethodMatch.let { + it.method.apply { + val index = it[0] - 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) videoQualityMethod else videoQualityLegacyMethod).apply { // Fix bad data used by YouTube. - it.method.addInstructions( + val nameRegister = if (is_20_20_or_greater) "p3" else "p2" + 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 - """ + """, ) // Add methods to access obfuscated quality fields. - it.classDef.apply { + classDef.apply { methods.add( ImmutableMethod( type, @@ -303,9 +320,9 @@ val videoInformationPatch = bytecodePatch( """ iget-object v0, p0, $qualityNameField return-object v0 - """ + """, ) - } + }, ) methods.add( @@ -328,28 +345,23 @@ val videoInformationPatch = bytecodePatch( """ iget v0, p0, $resolutionField return v0 - """ + """, ) - } + }, ) } } // Detect video quality changes and override the current quality. - setVideoQualityFingerprint.match( - videoQualitySetterFingerprint.originalClassDef - ).let { match -> + videoQualitySetterMethod.immutableClassDef.getSetVideoQualityMethod().let { + it // This instruction refers to the field with the type that contains the setQuality method. - val onItemClickListenerClassReference = match.method - .getInstruction(0).reference - val setQualityFieldReference = match.method - .getInstruction(1).reference as FieldReference + val onItemClickListenerClassReference = + it.getInstruction(0).reference + val setQualityFieldReference = + it.getInstruction(1).fieldReference!! - proxy( - classes.find { classDef -> - classDef.type == setQualityFieldReference.type - }!! - ).mutableClass.apply { + firstClassDef(setQualityFieldReference.type).apply { // Add interface and helper methods to allow extension code to call obfuscated methods. interfaces.add(EXTENSION_VIDEO_QUALITY_MENU_INTERFACE) @@ -358,7 +370,7 @@ val videoInformationPatch = bytecodePatch( type, "patch_setQuality", listOf( - ImmutableMethodParameter(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE, null, null) + ImmutableMethodParameter(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE, null, null), ), "V", AccessFlags.PUBLIC.value or AccessFlags.FINAL.value, @@ -375,13 +387,13 @@ val videoInformationPatch = bytecodePatch( """ invoke-virtual { p0, p1 }, $setQualityMenuIndexMethod return-void - """ + """, ) - } + }, ) } - videoQualitySetterFingerprint.method.addInstructions( + videoQualitySetterMethod.addInstructions( 0, """ # Get object instance to invoke setQuality method. @@ -390,7 +402,7 @@ val videoInformationPatch = bytecodePatch( invoke-static { p1, v0, p2 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([$YOUTUBE_VIDEO_QUALITY_CLASS_TYPE${EXTENSION_VIDEO_QUALITY_MENU_INTERFACE}I)I move-result p2 - """ + """, ) } @@ -400,7 +412,11 @@ val videoInformationPatch = bytecodePatch( } } -private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Method, seekToRelativeMethod: Method) { +private fun addSeekInterfaceMethods( + targetClass: MutableClassDef, + seekToMethod: Method, + seekToRelativeMethod: Method +) { // Add the interface and methods that extension calls. targetClass.interfaces.add(EXTENSION_PLAYER_INTERFACE) @@ -490,11 +506,10 @@ internal fun onCreateHookMdx(targetMethodClass: String, targetMethodName: String * @param targetMethodClass The descriptor for the static method to invoke when the player controller is created. * @param targetMethodName The name of the static method to invoke when the player controller is created. */ -fun videoTimeHook(targetMethodClass: String, targetMethodName: String) = - timeMethod.insertTimeHook( - timeInitInsertIndex++, - "$targetMethodClass->$targetMethodName(J)V", - ) +fun videoTimeHook(targetMethodClass: String, targetMethodName: String) = timeMethod.insertTimeHook( + timeInitInsertIndex++, + "$targetMethodClass->$targetMethodName(J)V", +) /** * Hook when the video speed is changed for any reason _except when the user manually selects a new speed_. @@ -502,7 +517,7 @@ fun videoTimeHook(targetMethodClass: String, targetMethodName: String) = fun videoSpeedChangedHook(targetMethodClass: String, targetMethodName: String) = setPlaybackSpeedMethod.addInstruction( setPlaybackSpeedMethodIndex++, - "invoke-static { p1 }, $targetMethodClass->$targetMethodName(F)V" + "invoke-static { p1 }, $targetMethodClass->$targetMethodName(F)V", ) /** @@ -511,11 +526,11 @@ fun videoSpeedChangedHook(targetMethodClass: String, targetMethodName: String) = fun userSelectedPlaybackSpeedHook(targetMethodClass: String, targetMethodName: String) { legacySpeedSelectionInsertMethod.addInstruction( legacySpeedSelectionInsertIndex++, - "invoke-static { v$legacySpeedSelectionValueRegister }, $targetMethodClass->$targetMethodName(F)V" + "invoke-static { v$legacySpeedSelectionValueRegister }, $targetMethodClass->$targetMethodName(F)V", ) speedSelectionInsertMethod.addInstruction( speedSelectionInsertIndex++, "invoke-static { v$speedSelectionValueRegister }, $targetMethodClass->$targetMethodName(F)V", ) -} \ No newline at end of file +} 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..cd78c70575 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,19 +1,19 @@ package app.revanced.patches.youtube.video.playerresponse -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext 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 { +internal val BytecodePatchContext.playerParameterBuilderMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters( - "Ljava/lang/String;", // VideoId. + returnType("L") + parameterTypes( + "Ljava/lang/String;", // VideoId. "[B", - "Ljava/lang/String;", // Player parameters proto buffer. + "Ljava/lang/String;", // Player parameters proto buffer. "Ljava/lang/String;", "I", "Z", @@ -25,21 +25,74 @@ 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;", ) - strings("psps") +} + +/** + * For targets 20.26 and later. + */ +internal val BytecodePatchContext.playerParameterBuilder2026Method by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("L") + parameterTypes( + "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("psps"()) +} + +/** + * For targets 20.15 to 20.25 + */ +internal val BytecodePatchContext.playerParameterBuilder2015Method by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("L") + parameterTypes( + "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("psps"()) } /** * For targets 20.10 to 20.14. */ -internal val playerParameterBuilder2010Fingerprint = fingerprint { +internal val BytecodePatchContext.playerParameterBuilder2010Method by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters( - "Ljava/lang/String;", // VideoId. + returnType("L") + parameterTypes( + "Ljava/lang/String;", // VideoId. "[B", - "Ljava/lang/String;", // Player parameters proto buffer. + "Ljava/lang/String;", // Player parameters proto buffer. "Ljava/lang/String;", "I", "Z", @@ -52,18 +105,18 @@ internal val playerParameterBuilder2010Fingerprint = fingerprint { "Z", // Appears to indicate if the video id is being opened or is currently playing. "Z", "Z", - "Z" + "Z", ) - strings("psps") + instructions("psps"()) } /** * For targets 20.02 to 20.09. */ -internal val playerParameterBuilder2002Fingerprint = fingerprint { +internal val BytecodePatchContext.playerParameterBuilder2002Method by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters( + returnType("L") + parameterTypes( "Ljava/lang/String;", // VideoId. "[B", "Ljava/lang/String;", // Player parameters proto buffer. @@ -80,16 +133,16 @@ internal val playerParameterBuilder2002Fingerprint = fingerprint { "Z", "Z", ) - strings("psps") + instructions("psps"()) } /** * For targets 19.25 to 19.50. */ -internal val playerParameterBuilder1925Fingerprint = fingerprint { +internal val BytecodePatchContext.playerParameterBuilder1925Method by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters( + returnType("L") + parameterTypes( "Ljava/lang/String;", // VideoId. "[B", "Ljava/lang/String;", // Player parameters proto buffer. @@ -105,16 +158,16 @@ internal val playerParameterBuilder1925Fingerprint = fingerprint { "Z", "Z", ) - strings("psps") + instructions("psps"()) } /** - * For targets 19.24 and earlier. + * For targets 19.01 to 19.24. */ -internal val playerParameterBuilderLegacyFingerprint = fingerprint { +internal val BytecodePatchContext.playerParameterBuilderLegacyMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters( + returnType("L") + parameterTypes( "Ljava/lang/String;", // VideoId. "[B", "Ljava/lang/String;", // Player parameters proto buffer. 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..e9b4503b49 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 @@ -1,15 +1,16 @@ package app.revanced.patches.youtube.video.playerresponse -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch 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() @@ -38,25 +39,31 @@ val playerResponseMethodHookPatch = bytecodePatch { versionCheckPatch, ) - execute { - val fingerprint : Fingerprint - if (is_20_15_or_greater) { + apply { + val method: MutableMethod + if (is_20_46_or_greater) { parameterIsShortAndOpeningOrPlaying = 13 - fingerprint = playerParameterBuilderFingerprint + method = playerParameterBuilderMethod + } else if (is_20_26_or_greater) { + parameterIsShortAndOpeningOrPlaying = 13 + method = playerParameterBuilder2026Method + } else if (is_20_15_or_greater) { + parameterIsShortAndOpeningOrPlaying = 13 + method = playerParameterBuilder2015Method } else if (is_20_10_or_greater) { parameterIsShortAndOpeningOrPlaying = 13 - fingerprint = playerParameterBuilder2010Fingerprint + method = playerParameterBuilder2010Method } else if (is_20_02_or_greater) { parameterIsShortAndOpeningOrPlaying = 12 - fingerprint = playerParameterBuilder2002Fingerprint + method = playerParameterBuilder2002Method } else if (is_19_23_or_greater) { parameterIsShortAndOpeningOrPlaying = 12 - fingerprint = playerParameterBuilder1925Fingerprint + method = playerParameterBuilder1925Method } else { parameterIsShortAndOpeningOrPlaying = 11 - fingerprint = playerParameterBuilderLegacyFingerprint + method = playerParameterBuilderLegacyMethod } - playerResponseMethod = fingerprint.method + playerResponseMethod = method // On some app targets the method has too many registers pushing the parameters past v15. // If needed, move the parameters to 4-bit registers, so they can be passed to the extension. @@ -74,7 +81,7 @@ val playerResponseMethodHookPatch = bytecodePatch { } } - finalize { + afterDependents { fun hookVideoId(hook: Hook) { playerResponseMethod.addInstruction( 0, 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 6397a1184c..77ac486946 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 @@ -1,19 +1,17 @@ package app.revanced.patches.youtube.video.quality -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.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.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.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.shared.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch @@ -25,67 +23,56 @@ 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;" private const val FILTER_CLASS_DESCRIPTOR = - "Lapp/revanced/extension/youtube/patches/components/AdvancedVideoQualityMenuFilter;" + "Lapp/revanced/extension/youtube/patches/litho/AdvancedVideoQualityMenuFilter;" internal val advancedVideoQualityMenuPatch = bytecodePatch { dependsOn( - advancedVideoQualityMenuResourcePatch, sharedExtensionPatch, settingsPatch, addResourcesPatch, lithoFilterPatch, recyclerViewTreeHookPatch, + resourceMappingPatch, ) - execute { + apply { addResources("youtube", "video.quality.advancedVideoQualityMenuPatch") settingsMenuVideoQualityGroup.add( - SwitchPreference("revanced_advanced_video_quality_menu") + SwitchPreference("revanced_advanced_video_quality_menu"), ) + // Used for the old type of the video quality menu. + videoQualityBottomSheetListFragmentTitle = + ResourceType.LAYOUT["video_quality_bottom_sheet_list_fragment_title"] + videoQualityQuickMenuAdvancedMenuDescription = + 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 { + videoQualityMenuViewInflateMethodMatch.let { it.method.apply { - val checkCastIndex = it.patternMatch!!.endIndex + val checkCastIndex = it[-1] val listViewRegister = getInstruction(checkCastIndex).registerA addInstruction( checkCastIndex + 1, "invoke-static { v$listViewRegister }, $EXTENSION_CLASS_DESCRIPTOR->" + - "addVideoQualityListMenuListener(Landroid/widget/ListView;)V", + "addVideoQualityListMenuListener(Landroid/widget/ListView;)V", ) } } // Force YT to add the 'advanced' quality menu for Shorts. - videoQualityMenuOptionsFingerprint.let { - val patternMatch = it.patternMatch!! - val startIndex = patternMatch.startIndex - val insertIndex = patternMatch.endIndex + videoQualityMenuOptionsMethodMatch.let { + val startIndex = it[0] + val insertIndex = it[-1] + if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex") it.method.apply { @@ -98,7 +85,7 @@ internal val advancedVideoQualityMenuPatch = bytecodePatch { """ invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation(Z)Z move-result v$register - """ + """, ) } } 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..f10565959c 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,35 +1,41 @@ package app.revanced.patches.youtube.video.quality -import app.revanced.patcher.fingerprint +import app.revanced.patcher.accessFlags +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.firstMethodDeclaratively +import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively +import app.revanced.patcher.name +import app.revanced.patcher.opcodes +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType 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.ClassDef -internal val videoQualityItemOnClickParentFingerprint = fingerprint { - returns("V") - strings("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT") +internal val BytecodePatchContext.videoQualityItemOnClickParentMethod by gettingFirstImmutableMethodDeclaratively( + "VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT", +) { + returnType("V") } -/** - * Resolves to class found in [videoQualityItemOnClickFingerprint]. - */ -internal val videoQualityItemOnClickFingerprint = fingerprint { - returns("V") - parameters( +context(_: BytecodePatchContext) +internal fun ClassDef.getVideoQualityItemOnClickMethod() = firstMethodDeclaratively { + name("onItemClick") + returnType("V") + parameterTypes( "Landroid/widget/AdapterView;", "Landroid/view/View;", "I", - "J" + "J", ) - custom { method, _ -> - method.name == "onItemClick" - } } -internal val videoQualityMenuOptionsFingerprint = fingerprint { +internal val BytecodePatchContext.videoQualityMenuOptionsMethodMatch by composingFirstMethod { accessFlags(AccessFlags.STATIC) - returns("[L") - parameters("Landroid/content/Context", "L", "L") + returnType("[L") + parameterTypes("Landroid/content/Context", "L", "L") opcodes( Opcode.CONST_4, // First instruction of method. Opcode.CONST_4, @@ -40,10 +46,10 @@ internal val videoQualityMenuOptionsFingerprint = fingerprint { literal { videoQualityQuickMenuAdvancedMenuDescription } } -internal val videoQualityMenuViewInflateFingerprint = fingerprint { +internal val BytecodePatchContext.videoQualityMenuViewInflateMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("L") - parameters("L", "L", "L") + returnType("L") + parameterTypes("L", "L", "L") opcodes( Opcode.INVOKE_SUPER, Opcode.CONST, 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..9b38ead454 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 @@ -1,7 +1,8 @@ package app.revanced.patches.youtube.video.quality -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.immutableClassDef import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch @@ -9,8 +10,9 @@ 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.versionCheckPatch import app.revanced.patches.youtube.misc.settings.settingsPatch -import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint +import app.revanced.patches.youtube.shared.videoQualityChangedMethodMatch import app.revanced.patches.youtube.video.information.onCreateHook import app.revanced.patches.youtube.video.information.videoInformationPatch import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -25,59 +27,58 @@ val rememberVideoQualityPatch = bytecodePatch { playerTypeHookPatch, settingsPatch, addResourcesPatch, + versionCheckPatch, ) - execute { + apply { addResources("youtube", "video.quality.rememberVideoQualityPatch") - settingsMenuVideoQualityGroup.addAll(listOf( - ListPreference( - key = "revanced_video_quality_default_mobile", - entriesKey = "revanced_video_quality_default_entries", - entryValuesKey = "revanced_video_quality_default_entry_values" - ), - ListPreference( - key = "revanced_video_quality_default_wifi", - entriesKey = "revanced_video_quality_default_entries", - entryValuesKey = "revanced_video_quality_default_entry_values" - ), - SwitchPreference("revanced_remember_video_quality_last_selected"), + settingsMenuVideoQualityGroup.addAll( + listOf( + ListPreference( + key = "revanced_video_quality_default_mobile", + entriesKey = "revanced_video_quality_default_entries", + entryValuesKey = "revanced_video_quality_default_entry_values", + ), + ListPreference( + key = "revanced_video_quality_default_wifi", + entriesKey = "revanced_video_quality_default_entries", + entryValuesKey = "revanced_video_quality_default_entry_values", + ), + SwitchPreference("revanced_remember_video_quality_last_selected"), - ListPreference( - key = "revanced_shorts_quality_default_mobile", - entriesKey = "revanced_shorts_quality_default_entries", - entryValuesKey = "revanced_shorts_quality_default_entry_values", + ListPreference( + key = "revanced_shorts_quality_default_mobile", + entriesKey = "revanced_shorts_quality_default_entries", + entryValuesKey = "revanced_shorts_quality_default_entry_values", + ), + ListPreference( + key = "revanced_shorts_quality_default_wifi", + entriesKey = "revanced_shorts_quality_default_entries", + entryValuesKey = "revanced_shorts_quality_default_entry_values", + ), + SwitchPreference("revanced_remember_shorts_quality_last_selected"), + SwitchPreference("revanced_remember_video_quality_last_selected_toast"), ), - ListPreference( - key = "revanced_shorts_quality_default_wifi", - entriesKey = "revanced_shorts_quality_default_entries", - entryValuesKey = "revanced_shorts_quality_default_entry_values" - ), - SwitchPreference("revanced_remember_shorts_quality_last_selected"), - SwitchPreference("revanced_remember_video_quality_last_selected_toast") - )) + ) onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted") // Inject a call to remember the selected quality for Shorts. - videoQualityItemOnClickFingerprint.match( - videoQualityItemOnClickParentFingerprint.classDef - ).method.addInstruction( + videoQualityItemOnClickParentMethod.immutableClassDef.getVideoQualityItemOnClickMethod().addInstruction( 0, - "invoke-static { p3 }, $EXTENSION_CLASS_DESCRIPTOR->userChangedShortsQuality(I)V" + "invoke-static { p3 }, $EXTENSION_CLASS_DESCRIPTOR->userChangedShortsQuality(I)V", ) // 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 + videoQualityChangedMethodMatch.let { match -> + val index = match[3] + val register = match.method.getInstruction(index).registerA - addInstruction( - index + 1, - "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V", - ) - } + match.method.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..f0cd306c14 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,9 +13,9 @@ import app.revanced.util.ResourceGroup import app.revanced.util.copyResources private val videoQualityButtonResourcePatch = resourcePatch { - dependsOn(playerControlsResourcePatch) + dependsOn(playerControlsPatch) - execute { + apply { copyResources( "qualitybutton", ResourceGroup( @@ -43,7 +43,7 @@ val videoQualityDialogButtonPatch = bytecodePatch( playerControlsPatch, ) - execute { + apply { addResources("youtube", "video.quality.button.videoQualityDialogButtonPatch") PreferenceScreen.PLAYER.addPreferences( 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..bc5639098b 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 @@ -14,24 +14,24 @@ internal val settingsMenuVideoQualityGroup = mutableSetOf() @Suppress("unused") val videoQualityPatch = bytecodePatch( name = "Video quality", - description = "Adds options to set default video qualities and always use the advanced video quality menu." + description = "Adds options to set default video qualities and always use the advanced video quality menu.", ) { dependsOn( rememberVideoQualityPatch, advancedVideoQualityMenuPatch, - videoQualityDialogButtonPatch + videoQualityDialogButtonPatch, ) 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 { + apply { PreferenceScreen.VIDEO.addPreferences( // Keep the preferences organized together. PreferenceCategory( @@ -39,8 +39,8 @@ val videoQualityPatch = bytecodePatch( titleKey = null, sorting = Sorting.UNSORTED, tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory", - preferences = settingsMenuVideoQualityGroup - ) + preferences = settingsMenuVideoQualityGroup, + ), ) } } 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..cecb755e0f 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,14 +28,14 @@ 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", ) ) - execute { + apply { PreferenceScreen.VIDEO.addPreferences( PreferenceCategory( key = "revanced_zz_video_key", // Dummy key to force the speed settings last. 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..98c425f894 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,9 +17,9 @@ import app.revanced.util.ResourceGroup import app.revanced.util.copyResources private val playbackSpeedButtonResourcePatch = resourcePatch { - dependsOn(playerControlsResourcePatch) + dependsOn(playerControlsPatch) - execute { + apply { copyResources( "speedbutton", ResourceGroup( @@ -48,7 +48,7 @@ val playbackSpeedButtonPatch = bytecodePatch( videoInformationPatch, ) - execute { + apply { addResources("youtube", "video.speed.button.playbackSpeedButtonPatch") PreferenceScreen.PLAYER.addPreferences( 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 9f905d6fc7..5f3273718f 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 @@ -1,59 +1,44 @@ package app.revanced.patches.youtube.video.speed.custom -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableField.Companion.toMutable +import app.revanced.patcher.classDef +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.immutableClassDef 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.misc.extension.sharedExtensionPatch import app.revanced.patches.shared.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.interaction.seekbar.customTapAndHoldMethodMatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch 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;" + "Lapp/revanced/extension/youtube/patches/litho/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,10 +49,10 @@ internal val customPlaybackSpeedPatch = bytecodePatch( lithoFilterPatch, versionCheckPatch, recyclerViewTreeHookPatch, - customPlaybackSpeedResourcePatch + resourceMappingPatch, ) - execute { + apply { addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch") settingsMenuVideoSpeedGroup.addAll( @@ -76,95 +61,90 @@ internal val customPlaybackSpeedPatch = bytecodePatch( SwitchPreference("revanced_restore_old_speed_menu"), TextPreference( "revanced_custom_playback_speeds", - inputType = InputType.TEXT_MULTI_LINE + 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) speedLimiterMethod else speedLimiterLegacyMethod).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 - replaceInstruction(limitMinIndex, "const/high16 v$limitMinRegister, 0.0f") replaceInstruction(limitMaxIndex, "const/high16 v$limitMaxRegister, 8.0f") } - - // 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") - - val arrayLengthConstIndex = indexOfFirstLiteralInstructionOrThrow(7) - val arrayLengthConstDestination = getInstruction(arrayLengthConstIndex).registerA - val playbackSpeedsArrayType = "$EXTENSION_CLASS_DESCRIPTOR->customPlaybackSpeeds:[F" - - addInstructions( - arrayLengthConstIndex + 1, - """ - 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 originalArrayFetchDestination = - getInstruction(originalArrayFetchIndex).registerA - - replaceInstruction( - originalArrayFetchIndex, - "sget-object v$originalArrayFetchDestination, $playbackSpeedsArrayType", - ) + // Turn off client side flag that use server provided min/max speeds. + if (is_20_34_or_greater) { + serverSideMaxSpeedFeatureFlagMethod.returnEarly(false) } // region Force old video quality menu. + // Replace the speeds float array with custom speeds. + speedArrayGeneratorMethodMatch.let { + it.method.apply { + val playbackSpeedsArrayType = "$EXTENSION_CLASS_DESCRIPTOR->customPlaybackSpeeds:[F" + // Apply changes from last index to first to preserve indexes. + + val originalArrayFetchIndex = it[5] + val originalArrayFetchDestination = getInstruction(it[5]).registerA + replaceInstruction( + originalArrayFetchIndex, + "sget-object v$originalArrayFetchDestination, $playbackSpeedsArrayType", + ) + + val arrayLengthConstDestination = getInstruction(it[3]).registerA + val newArrayIndex = it[4] + addInstructions( + newArrayIndex, + """ + sget-object v$arrayLengthConstDestination, $playbackSpeedsArrayType + array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination + """, + ) + + val sizeCallIndex = it[0] + 1 + val sizeCallResultRegister = getInstruction(sizeCallIndex).registerA + replaceInstruction(sizeCallIndex, "const/4 v$sizeCallResultRegister, 0x0") + } + } + // Add a static INSTANCE field to the class. // This is later used to call "showOldPlaybackSpeedMenu" on the instance. val instanceField = ImmutableField( - getOldPlaybackSpeedsFingerprint.originalClassDef.type, + getOldPlaybackSpeedsMethod.immutableClassDef.type, "INSTANCE", - getOldPlaybackSpeedsFingerprint.originalClassDef.type, + getOldPlaybackSpeedsMethod.immutableClassDef.type, AccessFlags.PUBLIC.value or AccessFlags.STATIC.value, null, null, null, ).toMutable() - getOldPlaybackSpeedsFingerprint.classDef.staticFields.add(instanceField) + getOldPlaybackSpeedsMethod.classDef.staticFields.add(instanceField) // Set the INSTANCE field to the instance of the class. // In order to prevent a conflict with another patch, add the instruction at index 1. - getOldPlaybackSpeedsFingerprint.method.addInstruction(1, "sput-object p0, $instanceField") + getOldPlaybackSpeedsMethod.addInstruction(1, "sput-object p0, $instanceField") // Get the "showOldPlaybackSpeedMenu" method. // This is later called on the field INSTANCE. - val showOldPlaybackSpeedMenuMethod = showOldPlaybackSpeedMenuFingerprint.match( - getOldPlaybackSpeedsFingerprint.classDef, - ).method + val showOldPlaybackSpeedMenuMethod = + getOldPlaybackSpeedsMethod.immutableClassDef.getShowOldPlaybackSpeedMenuMethod() // Insert the call to the "showOldPlaybackSpeedMenu" method on the field INSTANCE. - showOldPlaybackSpeedMenuExtensionFingerprint.method.apply { + showOldPlaybackSpeedMenuExtensionMethod.apply { addInstructionsWithLabels( instructions.lastIndex, """ @@ -173,34 +153,36 @@ internal val customPlaybackSpeedPatch = bytecodePatch( return-void :not_null invoke-virtual { v0 }, $showOldPlaybackSpeedMenuMethod - """ + """, ) } // 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() - } - val register = getInstruction(index).registerA + if (is_19_47_or_greater) { + customTapAndHoldMethodMatch.let { + it.method.apply { + val index = it[0] + val register = getInstruction(index).registerA - addInstructions( - index + 1, - """ - invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->tapAndHoldSpeed()F - move-result v$register - """ - ) + 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..c2db863a55 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,39 +1,71 @@ package app.revanced.patches.youtube.video.speed.custom -import app.revanced.patcher.fingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.shared.misc.mapping.ResourceType import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.reference.StringReference +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val getOldPlaybackSpeedsFingerprint = fingerprint { - parameters("[L", "I") - strings("menu_item_playback_speed") +internal val BytecodePatchContext.getOldPlaybackSpeedsMethod by gettingFirstMethodDeclaratively( + "menu_item_playback_speed", +) { + parameterTypes("[L", "I") } -internal val showOldPlaybackSpeedMenuFingerprint = fingerprint { - literal { speedUnavailableId } +context(_: BytecodePatchContext) +internal fun ClassDef.getShowOldPlaybackSpeedMenuMethod() = firstMethodDeclaratively { + instructions( + ResourceType.STRING("varispeed_unavailable_message"), + ) } -internal val showOldPlaybackSpeedMenuExtensionFingerprint = fingerprint { - custom { method, classDef -> - method.name == "showOldPlaybackSpeedMenu" && classDef.type == EXTENSION_CLASS_DESCRIPTOR - } +internal val BytecodePatchContext.showOldPlaybackSpeedMenuExtensionMethod by gettingFirstMethodDeclaratively { + name("showOldPlaybackSpeedMenu") } -internal val speedArrayGeneratorFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("[L") - parameters("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;") - strings("0.0#") -} - -internal val speedLimiterFingerprint = fingerprint { +internal val BytecodePatchContext.serverSideMaxSpeedFeatureFlagMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("F") + returnType("Z") + instructions( + 45719140L(), + ) +} + +internal val BytecodePatchContext.speedArrayGeneratorMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returnType("[L") + parameterTypes("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;") + instructions( + method { name == "size" && returnType == "I" }, + allOf(Opcode.NEW_INSTANCE(), type("Ljava/text/DecimalFormat;")), + "0.0#"(), + 7L(), + Opcode.NEW_ARRAY(), + field { definingClass.endsWith("/PlayerConfigModel;") && type == "[F" }, + ) +} + +/** + * 20.34+ + */ +internal val BytecodePatchContext.speedLimiterMethod by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("F", "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;") + instructions( + 0.25f.toRawBits().toLong()(), + 4.0f.toRawBits().toLong()(), + ) +} + +/** + * 20.33 and lower. + */ +internal val BytecodePatchContext.speedLimiterLegacyMethod by gettingFirstMethodDeclaratively { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("F") opcodes( Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT, @@ -45,16 +77,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..51e43993dd 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,10 @@ package app.revanced.patches.youtube.video.speed.remember -import app.revanced.patcher.fingerprint +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext -internal val initializePlaybackSpeedValuesFingerprint = fingerprint { - parameters("[L", "I") - strings("menu_item_playback_speed") +internal val BytecodePatchContext.initializePlaybackSpeedValuesMethod by gettingFirstMethodDeclaratively( + "menu_item_playback_speed", +) { + parameterTypes("[L", "I") } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt index 39160955cb..315da7fa0a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt @@ -1,9 +1,9 @@ package app.revanced.patches.youtube.video.speed.remember -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.ExternalLabel +import app.revanced.patcher.extensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -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.settings.preference.ListPreference @@ -24,10 +24,10 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch { settingsPatch, addResourcesPatch, videoInformationPatch, - customPlaybackSpeedPatch + customPlaybackSpeedPatch, ) - execute { + apply { addResources("youtube", "video.speed.remember.rememberPlaybackSpeedPatch") settingsMenuVideoSpeedGroup.addAll( @@ -37,11 +37,11 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch { // Entries and values are set by the extension code based on the actual speeds available. entriesKey = null, entryValuesKey = null, - tag = "app.revanced.extension.youtube.settings.preference.CustomVideoSpeedListPreference" + tag = "app.revanced.extension.youtube.settings.preference.CustomVideoSpeedListPreference", ), SwitchPreference("revanced_remember_playback_speed_last_selected"), - SwitchPreference("revanced_remember_playback_speed_last_selected_toast") - ) + SwitchPreference("revanced_remember_playback_speed_last_selected_toast"), + ), ) onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted") @@ -54,7 +54,7 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch { /* * Hook the code that is called when the playback speeds are initialized, and sets the playback speed */ - initializePlaybackSpeedValuesFingerprint.method.apply { + initializePlaybackSpeedValuesMethod.apply { // Infer everything necessary for calling the method setPlaybackSpeed(). val onItemClickListenerClassFieldReference = getInstruction(0).reference 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..535f38abc6 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,55 +1,55 @@ package app.revanced.patches.youtube.video.videoid -import app.revanced.patcher.fingerprint -import app.revanced.util.literal +import app.revanced.patcher.* +import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.ClassDef -internal val videoIdFingerprint = fingerprint { +/** + * Matches using the class found in [videoIdParentMethodMatch]. + */ +context(_: BytecodePatchContext) +internal fun ClassDef.getVideoIdMethodMatch() = firstMethodComposite { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("V") - parameters("L") - opcodes( - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT_OBJECT, + returnType("V") + parameterTypes("L") + instructions( + method { + definingClass == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" && + returnType == "Ljava/lang/String;" + }, + Opcode.MOVE_RESULT_OBJECT(), ) - custom { method, _ -> - method.indexOfPlayerResponseModelString() >= 0 - } } -internal val videoIdBackgroundPlayFingerprint = fingerprint { +internal val BytecodePatchContext.videoIdBackgroundPlayMethodMatch by composingFirstMethod { 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 + returnType("V") + parameterTypes("L") + instructions( + method { + definingClass == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" && + returnType == "Ljava/lang/String;" + }, + Opcode.MOVE_RESULT_OBJECT(), + Opcode.IPUT_OBJECT(), + Opcode.MONITOR_EXIT(), + Opcode.RETURN_VOID(), + Opcode.MONITOR_EXIT(), + 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 + custom { + immutableClassDef.methods.count() == 17 || // 20.39 and lower. + immutableClassDef.methods.count() == 16 // 20.40+ } - } -internal val videoIdParentFingerprint = fingerprint { +internal val BytecodePatchContext.videoIdParentMethodMatch by composingFirstMethod { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returns("[L") - parameters("L") - literal { 524288L } + returnType("[L") + parameterTypes("L") + instructions( + 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..76465dd9e9 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 @@ -1,19 +1,15 @@ package app.revanced.patches.youtube.video.videoid -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch 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. @@ -95,25 +91,21 @@ val videoIdPatch = bytecodePatch( playerResponseMethodHookPatch, ) - execute { - videoIdFingerprint.match(videoIdParentFingerprint.originalClassDef).method.apply { - videoIdMethod = this - val index = indexOfPlayerResponseModelString() - videoIdRegister = getInstruction(index + 1).registerA + apply { + videoIdParentMethodMatch.immutableClassDef.getVideoIdMethodMatch().let { + videoIdMethod = it.method + + val index = it[0] + videoIdRegister = it.method.getInstruction(index + 1).registerA videoIdInsertIndex = index + 2 } - videoIdBackgroundPlayFingerprint.method.apply { - backgroundPlaybackMethod = this - val index = indexOfPlayerResponseModelString() - backgroundPlaybackVideoIdRegister = getInstruction(index + 1).registerA + videoIdBackgroundPlayMethodMatch.let { + backgroundPlaybackMethod = it.method + + val index = it[0] + backgroundPlaybackVideoIdRegister = it.method.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;" -} 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 f5a39a9969..8ffe3e029f 100644 --- a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -1,22 +1,15 @@ package app.revanced.util -import app.revanced.patcher.FingerprintBuilder -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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.extensions.InstructionExtensions.instructions -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableClassDef +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableField +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableField.Companion.toMutable +import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod +import app.revanced.patcher.* +import app.revanced.patcher.extensions.* import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -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.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 @@ -25,6 +18,7 @@ import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode.* import com.android.tools.smali.dexlib2.analysis.reflection.util.ReflectionUtils import com.android.tools.smali.dexlib2.formatter.DexFormatter +import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.* import com.android.tools.smali.dexlib2.iface.reference.FieldReference @@ -34,8 +28,8 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference import com.android.tools.smali.dexlib2.iface.value.* import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.value.* -import com.android.tools.smali.dexlib2.util.MethodUtil import java.util.* +import kotlin.collections.ArrayDeque /** * Starting from and including the instruction at index [startIndex], @@ -94,7 +88,10 @@ 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) { @@ -111,8 +108,10 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In // Somehow every method register was read from before any register was wrote to. // In practice this never occurs. - throw IllegalArgumentException("Could not find a free register from startIndex: " + - "$startIndex excluding: $registersToExclude") + throw IllegalArgumentException( + "Could not find a free register from startIndex: " + + "$startIndex excluding: $registersToExclude", + ) } } @@ -139,9 +138,13 @@ internal val Instruction.registersUsed: List } is ThreeRegisterInstruction -> listOf(registerA, registerB, registerC) + is TwoRegisterInstruction -> listOf(registerA, registerB) + is OneRegisterInstruction -> listOf(registerA) + is RegisterRangeInstruction -> (startRegister until (startRegister + registerCount)).toList() + else -> emptyList() } @@ -217,7 +220,8 @@ private fun Method.findInstructionIndexFromToString(fieldName: String): Int { val fieldSetOpcode = getInstruction(fieldSetIndex).opcode if (fieldSetOpcode == MOVE_RESULT || fieldSetOpcode == MOVE_RESULT_WIDE || - fieldSetOpcode == MOVE_RESULT_OBJECT) { + fieldSetOpcode == MOVE_RESULT_OBJECT + ) { fieldSetIndex-- } @@ -229,10 +233,10 @@ private fun Method.findInstructionIndexFromToString(fieldName: String): Int { * * @param fieldName The name of the field to find. Partial matches are allowed. */ -context(BytecodePatchContext) +context(context: BytecodePatchContext) internal fun Method.findMethodFromToString(fieldName: String): MutableMethod { val methodUsageIndex = findInstructionIndexFromToString(fieldName) - return navigate(this).to(methodUsageIndex).stop() + return context.navigate(this).to(methodUsageIndex).stop() } /** @@ -248,28 +252,16 @@ internal fun Method.findFieldFromToString(fieldName: String): FieldReference { /** * Adds public [AccessFlags] and removes private and protected flags (if present). */ -internal fun Int.toPublicAccessFlags(): Int { - return this.or(AccessFlags.PUBLIC.value) - .and(AccessFlags.PROTECTED.value.inv()) - .and(AccessFlags.PRIVATE.value.inv()) -} - -/** - * Find the [MutableMethod] from a given [Method] in a [MutableClass]. - * - * @param method The [Method] to find. - * @return The [MutableMethod]. - */ -fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first { - MethodUtil.methodSignaturesMatch(it, method) -} +internal fun Int.toPublicAccessFlags(): Int = this.or(AccessFlags.PUBLIC.value) + .and(AccessFlags.PROTECTED.value.inv()) + .and(AccessFlags.PRIVATE.value.inv()) /** * Apply a transform to all methods of the class. * * @param transform The transformation function. Accepts a [MutableMethod] and returns a transformed [MutableMethod]. */ -fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) { +fun MutableClassDef.transformMethods(transform: MutableMethod.() -> MutableMethod) { val transformedMethods = methods.map { it.transform() } methods.clear() methods.addAll(transformedMethods) @@ -293,7 +285,6 @@ fun MutableMethod.injectHideViewCall( "invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V", ) - /** * Inserts instructions at a given index, using the existing control flow label at that index. * Inserted instructions can have it's own control flow labels as well. @@ -310,7 +301,7 @@ fun MutableMethod.injectHideViewCall( // TODO: delete this on next major version bump. fun MutableMethod.addInstructionsAtControlFlowLabel( insertIndex: Int, - instructions: String + instructions: String, ) = addInstructionsAtControlFlowLabel(insertIndex, instructions, *arrayOf()) /** @@ -329,7 +320,7 @@ fun MutableMethod.addInstructionsAtControlFlowLabel( fun MutableMethod.addInstructionsAtControlFlowLabel( insertIndex: Int, instructions: String, - vararg externalLabels: ExternalLabel + vararg externalLabels: ExternalLabel, ) { // Duplicate original instruction and add to +1 index. addInstruction(insertIndex + 1, getInstruction(insertIndex)) @@ -355,10 +346,8 @@ fun MutableMethod.addInstructionsAtControlFlowLabel( * @throws PatchException if the resource cannot be found. * @see [indexOfFirstResourceIdOrThrow], [indexOfFirstLiteralInstructionReversed] */ -fun Method.indexOfFirstResourceId(resourceName: String): Int { - val resourceId = resourceMappings["id", resourceName] - return indexOfFirstLiteralInstruction(resourceId) -} +fun Method.indexOfFirstResourceId(resourceName: String): Int = + indexOfFirstLiteralInstruction(ResourceType.ID[resourceName]) /** * Get the index of the first instruction with the id of the given resource name or throw a [PatchException]. @@ -407,8 +396,7 @@ fun Method.indexOfFirstLiteralInstructionOrThrow(literal: Long): Int { * @return the first literal instruction with the value, or -1 if not found. * @see indexOfFirstLiteralInstructionOrThrow */ -fun Method.indexOfFirstLiteralInstruction(literal: Float) = - indexOfFirstLiteralInstruction(literal.toRawBits().toLong()) +fun Method.indexOfFirstLiteralInstruction(literal: Float) = indexOfFirstLiteralInstruction(literal.toRawBits().toLong()) /** * Find the index of the first literal instruction with the given float value, @@ -428,8 +416,7 @@ fun Method.indexOfFirstLiteralInstructionOrThrow(literal: Float): Int { * @return the first literal instruction with the value, or -1 if not found. * @see indexOfFirstLiteralInstructionOrThrow */ -fun Method.indexOfFirstLiteralInstruction(literal: Double) = - indexOfFirstLiteralInstruction(literal.toRawBits()) +fun Method.indexOfFirstLiteralInstruction(literal: Double) = indexOfFirstLiteralInstruction(literal.toRawBits()) /** * Find the index of the first literal instruction with the given double value, @@ -536,12 +523,12 @@ fun Method.containsLiteralInstruction(literal: Double) = indexOfFirstLiteralInst * @param targetClass the class to start traversing the class hierarchy from. * @param callback function that is called for every class in the hierarchy. */ -fun BytecodePatchContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) { +fun BytecodePatchContext.traverseClassHierarchy(targetClass: MutableClassDef, callback: MutableClassDef.() -> Unit) { callback(targetClass) targetClass.superclass ?: return - classBy { targetClass.superclass == it.type }?.mutableClass?.let { + firstClassDefOrNull(targetClass.superclass!!)?.let { traverseClassHierarchy(it, callback) } } @@ -753,8 +740,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 @@ -769,7 +760,7 @@ internal fun MutableMethod.insertLiteralOverride(literal: Long, extensionMethodD """ $operation, $extensionMethodDescriptor move-result v$register - """ + """, ) } @@ -778,61 +769,49 @@ 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" addInstruction( index + 1, - "const v$register, $overrideValue" + "const v$register, $overrideValue", ) } /** - * Called for _all_ methods with the given literal value. - * Method indices are iterated from last to first. + * Iterates all instructions as sequence in all methods of all classes in the [BytecodePatchContext]. + * + * @param match A function that matches instructions. If a match is found, it returns a value of type [T], otherwise null. + * @param transform A function that transforms the matched instruction using the mutable method and the matched value + * of type [T]. */ -fun BytecodePatchContext.forEachLiteralValueInstruction( - literal: Long, - block: MutableMethod.(matchingIndex: Int) -> Unit, +fun BytecodePatchContext.forEachInstructionAsSequence( + match: (classDef: ClassDef, method: Method, instruction: Instruction, index: Int) -> T?, + transform: (MutableMethod, T) -> Unit ) { - val matchingIndexes = ArrayList() + classDefs.flatMap { classDef -> + classDef.methods.mapNotNull { method -> + val matches = method.instructionsOrNull?.mapIndexedNotNull { index, instruction -> + match(classDef, method, instruction, index) + } ?: return@mapNotNull null - classes.forEach { classDef -> - classDef.methods.forEach { method -> - method.implementation?.instructions?.let { instructions -> - matchingIndexes.clear() - - instructions.forEachIndexed { index, instruction -> - if ((instruction as? WideLiteralInstruction)?.wideLiteral == literal) { - matchingIndexes.add(index) - } - } - - 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. - - matchingIndexes.asReversed().forEach { index -> - block.invoke(mutableMethod, index) - } - } - } + if (matches.any()) method to matches else null } - } + }.forEach { (method, matches) -> + val method = firstMethod(method) + val matches = matches.toCollection(ArrayDeque()) + + while (!matches.isEmpty()) transform(method, matches.removeLast()) + } } private fun MutableMethod.checkReturnType(expectedTypes: Iterable>) { @@ -1231,16 +1210,13 @@ internal fun MutableField.removeFlags(vararg flags: AccessFlags) { } internal fun BytecodePatchContext.addStaticFieldToExtension( - className: String, + type: String, methodName: String, fieldName: String, objectClass: String, - smaliInstructions: 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 = firstClassDef(type) val objectCall = "$mutableClass->$fieldName:$objectClass" mutableClass.apply { @@ -1253,30 +1229,28 @@ internal fun BytecodePatchContext.addStaticFieldToExtension( AccessFlags.PUBLIC.value or AccessFlags.STATIC.value, null, annotations, - null - ).toMutable() + null, + ).toMutable(), ) addInstructionsWithLabels( 0, """ sget-object v0, $objectCall - """ + smaliInstructions + """ + smaliInstructions, ) } } } /** - * Set the custom condition for this fingerprint to check for a literal value. + * Set the custom condition for this predicate to check for a literal value. * * @param literalSupplier The supplier for the literal value to check for. */ -// TODO: add a way for subclasses to also use their own custom fingerprint. -fun FingerprintBuilder.literal(literalSupplier: () -> Long) { - custom { method, _ -> - method.containsLiteralInstruction(literalSupplier()) - } +@Deprecated("Instead use `literal()`") +fun MutablePredicateList.literal(literalSupplier: () -> Long) { + custom { containsLiteralInstruction(literalSupplier()) } } private class InstructionUtils { @@ -1285,12 +1259,16 @@ private class InstructionUtils { GOTO, GOTO_16, GOTO_32, IF_EQ, IF_NE, IF_LT, IF_GE, IF_GT, IF_LE, IF_EQZ, IF_NEZ, IF_LTZ, IF_GEZ, IF_GTZ, IF_LEZ, - PACKED_SWITCH_PAYLOAD, SPARSE_SWITCH_PAYLOAD + PACKED_SWITCH_PAYLOAD, SPARSE_SWITCH_PAYLOAD, ) val returnOpcodes: EnumSet = EnumSet.of( - RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT, RETURN_VOID_NO_BARRIER, - THROW + RETURN_VOID, + RETURN, + RETURN_WIDE, + RETURN_OBJECT, + RETURN_VOID_NO_BARRIER, + THROW, ) val writeOpcodes: EnumSet = EnumSet.of( diff --git a/patches/src/main/kotlin/app/revanced/util/resource/StringResource.kt b/patches/src/main/kotlin/app/revanced/util/resource/StringResource.kt index cb9c0b213d..c0956257c3 100644 --- a/patches/src/main/kotlin/app/revanced/util/resource/StringResource.kt +++ b/patches/src/main/kotlin/app/revanced/util/resource/StringResource.kt @@ -17,40 +17,40 @@ class StringResource( val value: String, val formatted: Boolean = true, ) : BaseResource(name, "string") { - override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = - super.serialize(ownerDocument, resourceCallback).apply { - - fun String.validateAndroidStringEscaping() : String { - if (value.startsWith('"') && value.endsWith('"')) { - // Raw strings allow unescaped single quote but not double quote. - if (!value.substring(1, value.length - 1).contains(Regex("(? Unit) = super.serialize(ownerDocument, resourceCallback).apply { + fun String.validateAndroidStringEscaping(): String { + if (value.startsWith('"') && value.endsWith('"')) { + // Raw strings allow unescaped single quote but not double quote. + if (!value.substring(1, value.length - 1).contains(Regex("(?@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 ce372be397..38d270a1d0 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 @@ -772,6 +775,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 @@ -891,6 +897,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 4ab39e0f47..edfcf40ede 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,18 +2,19 @@ rootProject.name = "revanced-patches" pluginManagement { repositories { + mavenLocal() gradlePluginPortal() google() maven { name = "githubPackages" - url = uri("https://maven.pkg.github.com/revanced/registry") + url = uri("https://maven.pkg.github.com/revanced/revanced-patches") credentials(PasswordCredentials::class) } } } plugins { - id("app.revanced.patches") version "1.0.0-dev.7" + id("app.revanced.patches") version "1.0.0-dev.9" } settings {