diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml
index 250871bcc..193a26af0 100644
--- a/.github/workflows/build_pull_request.yml
+++ b/.github/workflows/build_pull_request.yml
@@ -16,6 +16,12 @@ jobs:
with:
fetch-depth: 0
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: "temurin"
+ java-version: "17"
+
- name: Cache Gradle
uses: burrunan/gradle-cache-action@v1
diff --git a/.github/workflows/open_pull_request.yml b/.github/workflows/open_pull_request.yml
index 2afa0596c..33c8a7211 100644
--- a/.github/workflows/open_pull_request.yml
+++ b/.github/workflows/open_pull_request.yml
@@ -20,13 +20,12 @@ jobs:
- name: Open pull request
uses: repo-sync/pull-request@v2
with:
- destination_branch: "main"
- pr_title: "chore: ${{ env.MESSAGE }}"
+ destination_branch: main
+ pr_title: 'chore: ${{ env.MESSAGE }}'
pr_body: |
This pull request will ${{ env.MESSAGE }}.
## Before merging this PR
- - [ ] Remember about https://github.com/revanced/revanced-integrations
- [ ] Pull translations from Crowdin
pr_draft: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b210aad5c..498cca413 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -23,13 +23,19 @@ jobs:
persist-credentials: false
fetch-depth: 0
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: "temurin"
+ java-version: "17"
+
- name: Cache Gradle
uses: burrunan/gradle-cache-action@v1
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: ./gradlew generateMeta clean
+ run: ./gradlew build clean
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.gitignore b/.gitignore
index 501541766..62f6eb424 100644
--- a/.gitignore
+++ b/.gitignore
@@ -122,5 +122,8 @@ gradle-app.setting
# Dependency directories
node_modules/
-# gradle properties, due to Github token
+# Gradle properties, due to Github token
./gradle.properties
+
+# One package is called the same as the Gradle build folder
+!**/src/**/build/
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index e086a70c4..f1854e4b5 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -4,5 +4,5 @@
-
+
\ No newline at end of file
diff --git a/.releaserc b/.releaserc
index ff81c99ad..b3d61b10b 100644
--- a/.releaserc
+++ b/.releaserc
@@ -23,7 +23,6 @@
"assets": [
"CHANGELOG.md",
"gradle.properties",
- "patches.json"
],
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
@@ -33,11 +32,8 @@
{
"assets": [
{
- "path": "build/libs/revanced-patches*"
+ "path": "patches/build/libs/patches-!(*sources*|*javadoc*).rvp?(.asc)"
},
- {
- "path": "patches.json"
- }
],
successComment: false
}
diff --git a/api/revanced-patches.api b/api/revanced-patches.api
deleted file mode 100644
index 0b4976b98..000000000
--- a/api/revanced-patches.api
+++ /dev/null
@@ -1,2308 +0,0 @@
-public final class app/revanced/generator/MainKt {
- public static synthetic fun main ([Ljava/lang/String;)V
-}
-
-public final class app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/all/connectivity/wifi/spoof/SpoofWifiPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/all/connectivity/wifi/spoof/SpoofWifiPatch;
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V
-}
-
-public final class app/revanced/patches/all/directory/ChangeDataDirectoryLocationPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/all/directory/ChangeDataDirectoryLocationPatch;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Integer;
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
-}
-
-public final class app/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/all/location/hide/HideMockLocationPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/all/location/hide/HideMockLocationPatch;
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V
-}
-
-public abstract class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public fun ()V
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair;
- protected fun getBoard ()Ljava/lang/String;
- protected fun getBootloader ()Ljava/lang/String;
- protected fun getBrand ()Ljava/lang/String;
- protected fun getCpuAbi ()Ljava/lang/String;
- protected fun getCpuAbi2 ()Ljava/lang/String;
- protected fun getDevice ()Ljava/lang/String;
- protected fun getDisplay ()Ljava/lang/String;
- protected fun getFingerprint ()Ljava/lang/String;
- protected fun getHardware ()Ljava/lang/String;
- protected fun getHost ()Ljava/lang/String;
- protected fun getId ()Ljava/lang/String;
- protected fun getManufacturer ()Ljava/lang/String;
- protected fun getModel ()Ljava/lang/String;
- protected fun getOdmSku ()Ljava/lang/String;
- protected fun getProduct ()Ljava/lang/String;
- protected fun getRadio ()Ljava/lang/String;
- protected fun getSerial ()Ljava/lang/String;
- protected fun getSku ()Ljava/lang/String;
- protected fun getSocManufacturer ()Ljava/lang/String;
- protected fun getSocModel ()Ljava/lang/String;
- protected fun getTags ()Ljava/lang/String;
- protected fun getTime ()Ljava/lang/Long;
- protected fun getType ()Ljava/lang/String;
- protected fun getUser ()Ljava/lang/String;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V
-}
-
-public final class app/revanced/patches/all/misc/build/SpoofBuildInfoPatch : app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch {
- public fun ()V
-}
-
-public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/all/misc/hex/HexPatch : app/revanced/patches/shared/misc/hex/BaseHexPatch {
- public fun ()V
- public fun getReplacements ()Ljava/util/List;
-}
-
-public final class app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/all/misc/network/OverrideCertificatePinningPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
- public static final field INSTANCE Lapp/revanced/patches/all/misc/packagename/ChangePackageNamePatch;
- public fun close ()V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
- public final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String;
-}
-
-public final class app/revanced/patches/all/misc/resources/AddResourcesPatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable, java/util/Map, kotlin/jvm/internal/markers/KMutableMap {
- public static final field INSTANCE Lapp/revanced/patches/all/misc/resources/AddResourcesPatch;
- public fun clear ()V
- public fun close ()V
- public final fun containsKey (Ljava/lang/Object;)Z
- public fun containsKey (Ljava/lang/String;)Z
- public final fun containsValue (Ljava/lang/Object;)Z
- public fun containsValue (Ljava/util/Set;)Z
- public final fun entrySet ()Ljava/util/Set;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
- public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
- public final fun get (Ljava/lang/Object;)Ljava/util/Set;
- public fun get (Ljava/lang/String;)Ljava/util/Set;
- public fun getEntries ()Ljava/util/Set;
- public fun getKeys ()Ljava/util/Set;
- public fun getSize ()I
- public fun getValues ()Ljava/util/Collection;
- public final fun invoke (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
- public final fun invoke (Ljava/lang/String;Ljava/lang/Iterable;)Z
- public final fun invoke (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Z
- public final fun invoke (Ljava/lang/String;Ljava/util/List;)Z
- public final fun invoke (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Z
- public static synthetic fun invoke$default (Lapp/revanced/patches/all/misc/resources/AddResourcesPatch;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;ILjava/lang/Object;)Z
- public static synthetic fun invoke$default (Lapp/revanced/patches/all/misc/resources/AddResourcesPatch;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Z
- public fun isEmpty ()Z
- public final fun keySet ()Ljava/util/Set;
- public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- public fun put (Ljava/lang/String;Ljava/util/Set;)Ljava/util/Set;
- public fun putAll (Ljava/util/Map;)V
- public final synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
- public final fun remove (Ljava/lang/Object;)Ljava/util/Set;
- public fun remove (Ljava/lang/String;)Ljava/util/Set;
- public final fun size ()I
- public final fun values ()Ljava/util/Collection;
-}
-
-public abstract class app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun ()V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public abstract fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public final fun findPatchIndices (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;)Lkotlin/sequences/Sequence;
- public abstract fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
-}
-
-public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall {
- public abstract fun getDefinedClassName ()Ljava/lang/String;
- public abstract fun getMethodName ()Ljava/lang/String;
- public abstract fun getMethodParams ()[Ljava/lang/String;
- public abstract fun getReturnType ()Ljava/lang/String;
- public abstract fun replaceInvokeVirtualWithIntegrations (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/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 replaceInvokeVirtualWithIntegrations (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 final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch;
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V
-}
-
-public final class app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall : java/lang/Enum, app/revanced/patches/all/misc/transformation/IMethodCall {
- public static final field SetAllowedCapturePolicyGlobal Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall;
- public static final field SetAllowedCapturePolicySingle Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall;
- public fun getDefinedClassName ()Ljava/lang/String;
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
- public fun getMethodName ()Ljava/lang/String;
- public fun getMethodParams ()[Ljava/lang/String;
- public fun getReturnType ()Ljava/lang/String;
- public fun replaceInvokeVirtualWithIntegrations (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V
- public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall;
- public static fun values ()[Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall;
-}
-
-public final class app/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V
-}
-
-public final class app/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall : java/lang/Enum, app/revanced/patches/all/misc/transformation/IMethodCall {
- public static final field AddFlags Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall;
- public static final field SetFlags Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall;
- public fun getDefinedClassName ()Ljava/lang/String;
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
- public fun getMethodName ()Ljava/lang/String;
- public fun getMethodParams ()[Ljava/lang/String;
- public fun getReturnType ()Ljava/lang/String;
- public fun replaceInvokeVirtualWithIntegrations (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V
- public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall;
- public static fun values ()[Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall;
-}
-
-public final class app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryPatch;
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V
-}
-
-public final class app/revanced/patches/amazon/deeplinking/DeepLinkingPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/amazon/deeplinking/DeepLinkingPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/backdrops/misc/pro/ProUnlockPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/candylinkvpn/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/candylinkvpn/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/duolingo/ad/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/duolingo/ad/DisableAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/duolingo/debug/EnableDebugMenuPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/facebook/ads/story/HideStoryAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/facebook/ads/story/HideStoryAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/finanzonline/detection/root/RootDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/finanzonline/detection/root/RootDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/googlenews/customtabs/EnableCustomTabs : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/googlenews/customtabs/EnableCustomTabs;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch {
- public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch;
-}
-
-public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/gms/GmsCoreSupportResourcePatch;
-}
-
-public final class app/revanced/patches/googlenews/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/googlephotos/features/SpoofFeaturesPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/SpoofFeaturesPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
- public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint;
-}
-
-public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch {
- public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch;
-}
-
-public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/gms/GmsCoreSupportResourcePatch;
-}
-
-public final class app/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/googlephotos/preferences/RestoreHiddenBackUpWhileChargingTogglePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/googlephotos/preferences/RestoreHiddenBackUpWhileChargingTogglePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/hexeditor/ad/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/hexeditor/ad/DisableAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/idaustria/detection/root/RootDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/idaustria/detection/root/RootDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/inshorts/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/inshorts/ad/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/instagram/patches/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/instagram/patches/ad/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/instagram/patches/ads/timeline/HideTimelineAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/instagram/patches/ads/timeline/HideTimelineAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/irplus/ad/RemoveAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/irplus/ad/RemoveAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/memegenerator/detection/license/LicenseValidationPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/messenger/inbox/HideInboxAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/messenger/inbox/HideInboxAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/messenger/inbox/HideInboxSubtabsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/mifitness/misc/login/FixLoginPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/mifitness/misc/login/FixLoginPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/moneymanager/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/moneymanager/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/ad/video/HideMusicVideoAds : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/ad/video/HideMusicVideoAds;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/ad/video/HideVideoAds : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/ad/video/HideVideoAds;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/ad/video/MusicVideoAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/ad/video/MusicVideoAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/audio/codecs/CodecsUnlockPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/audio/codecs/CodecsUnlockPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/layout/compactheader/CompactHeaderPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/layout/compactheader/CompactHeaderPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/layout/compactheader/HideCategoryBar : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/layout/compactheader/HideCategoryBar;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/layout/premium/HideGetPremiumPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/layout/premium/HideGetPremiumPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/music/misc/gms/Constants {
- public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/Constants;
-}
-
-public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch {
- public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/GmsCoreSupportPatch;
-}
-
-public final class app/revanced/patches/music/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/GmsCoreSupportResourcePatch;
-}
-
-public final class app/revanced/patches/music/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/music/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/myexpenses/misc/pro/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/myfitnesspal/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/myfitnesspal/ads/fingerprints/IsPremiumUseCaseImplFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
- public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/fingerprints/IsPremiumUseCaseImplFingerprint;
-}
-
-public final class app/revanced/patches/myfitnesspal/ads/fingerprints/MainActivityNavigateToNativePremiumUpsellFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
- public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/fingerprints/MainActivityNavigateToNativePremiumUpsellFingerprint;
-}
-
-public final class app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/nyx/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/nyx/misc/pro/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/photomath/detection/signature/SignatureDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/piccomafr/tracking/DisableTrackingPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/piccomafr/tracking/DisableTrackingPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/pixiv/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/pixiv/ads/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/fingerprints/ShowReminderFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
- public static final field INSTANCE Lapp/revanced/patches/rar/misc/annoyances/purchasereminder/fingerprints/ShowReminderFingerprint;
-}
-
-public final class app/revanced/patches/reddit/ad/banner/HideBannerPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/ad/banner/HideBannerPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/ad/comments/HideCommentAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/ad/general/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/ad/general/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public abstract class app/revanced/patches/reddit/customclients/BaseFixSLinksPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Ljava/util/Set;)V
- public synthetic fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- protected abstract fun getIntegrationsClassDescriptor ()Ljava/lang/String;
- protected final fun getResolveSLinkMethod ()Ljava/lang/String;
- protected final fun getSetAccessTokenMethod ()Ljava/lang/String;
- protected abstract fun patchNavigationHandler (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V
- protected abstract fun patchSetAccessToken (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public abstract class app/revanced/patches/reddit/customclients/BaseSpoofClientPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V
- public synthetic fun (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public final fun getClientId ()Ljava/lang/String;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public final fun setClientId (Ljava/lang/String;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/Constants {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/Constants;
- public static final field OAUTH_USER_AGENT Ljava/lang/String;
-}
-
-public abstract class app/revanced/patches/reddit/customclients/ads/BaseDisableAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Ljava/util/Set;Ljava/util/Set;)V
- public synthetic fun (Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch;
-}
-
-public final class app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/joeyforreddit/api/fingerprints/AuthUtilityUserAgent : app/revanced/patcher/fingerprint/MethodFingerprint {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/api/fingerprints/AuthUtilityUserAgent;
-}
-
-public final class app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforlemmy/ads/DisableAdsPatch : app/revanced/patches/reddit/customclients/ads/BaseDisableAdsPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforlemmy/ads/DisableAdsPatch;
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/ads/DisableAdsPatch : app/revanced/patches/reddit/customclients/ads/BaseDisableAdsPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/ads/DisableAdsPatch;
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/api/SpoofClientPatch;
- public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
- public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/DisablePiracyDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/DisablePiracyDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch;
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/user/UseUserEndpointPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/user/UseUserEndpointPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/scbeasy/detection/debugging/RemoveDebuggingDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/scbeasy/detection/debugging/RemoveDebuggingDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public abstract class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public abstract class app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lkotlin/reflect/KClass;Lapp/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lkotlin/reflect/KClass;Lapp/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public abstract class app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch : app/revanced/patcher/patch/ResourcePatch {
- public fun (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/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
- protected final fun getGmsCoreVendor ()Ljava/lang/String;
- protected final fun getGmsCoreVendorGroupId ()Ljava/lang/String;
-}
-
-public abstract class app/revanced/patches/shared/misc/hex/BaseHexPatch : app/revanced/patcher/patch/RawResourcePatch {
- public fun ()V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
- public abstract fun getReplacements ()Ljava/util/List;
-}
-
-public final class app/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement {
- public static final field Companion Lapp/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion;
- public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
- public final fun replacePattern ([B)V
-}
-
-public final class app/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion {
-}
-
-public abstract class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Ljava/lang/String;Ljava/util/Set;)V
- public fun (Ljava/util/Set;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public abstract class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
- public fun ()V
- public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun invoke (Ljava/lang/String;)V
-}
-
-public abstract interface class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IHookInsertIndexResolver : kotlin/jvm/functions/Function1 {
- public abstract fun invoke (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer;
-}
-
-public final class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IHookInsertIndexResolver$DefaultImpls {
- public static fun invoke (Lapp/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IHookInsertIndexResolver;Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer;
-}
-
-public abstract interface class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IRegisterResolver : kotlin/jvm/functions/Function1 {
- public abstract fun invoke (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer;
-}
-
-public final class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IRegisterResolver$DefaultImpls {
- public static fun invoke (Lapp/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IRegisterResolver;Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer;
-}
-
-public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/shared/misc/mapping/ResourceMappingPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
- public final fun get (Ljava/lang/String;Ljava/lang/String;)J
-}
-
-public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch$ResourceElement {
- public fun (Ljava/lang/String;Ljava/lang/String;J)V
- public final fun component1 ()Ljava/lang/String;
- 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/ResourceMappingPatch$ResourceElement;
- public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/mapping/ResourceMappingPatch$ResourceElement;Ljava/lang/String;Ljava/lang/String;JILjava/lang/Object;)Lapp/revanced/patches/shared/misc/mapping/ResourceMappingPatch$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 fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public abstract class app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable, java/util/Set, kotlin/jvm/internal/markers/KMutableSet {
- public fun ()V
- public fun (Lkotlin/Pair;Ljava/util/Set;)V
- public synthetic fun (Lkotlin/Pair;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun add (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)Z
- public synthetic fun add (Ljava/lang/Object;)Z
- public fun addAll (Ljava/util/Collection;)Z
- public fun clear ()V
- public fun close ()V
- public fun contains (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)Z
- public final fun contains (Ljava/lang/Object;)Z
- public fun containsAll (Ljava/util/Collection;)Z
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
- public fun getSize ()I
- public fun isEmpty ()Z
- public fun iterator ()Ljava/util/Iterator;
- public fun remove (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)Z
- public final fun remove (Ljava/lang/Object;)Z
- public fun removeAll (Ljava/util/Collection;)Z
- public fun retainAll (Ljava/util/Collection;)Z
- public final fun size ()I
- public fun toArray ()[Ljava/lang/Object;
- public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
-}
-
-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;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun equals (Ljava/lang/Object;)Z
- public final fun getKey ()Ljava/lang/String;
- public final fun getSummaryKey ()Ljava/lang/String;
- public final fun getTag ()Ljava/lang/String;
- public final fun getTitleKey ()Ljava/lang/String;
- public fun hashCode ()I
- public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
-}
-
-public final class app/revanced/patches/shared/misc/settings/preference/BasePreference$Companion {
- public final fun addSummary (Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;)V
- public static synthetic fun addSummary$default (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;ILjava/lang/Object;)V
-}
-
-public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen : java/io/Closeable {
- public fun ()V
- public fun (Ljava/util/Set;)V
- public synthetic fun (Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun close ()V
- public abstract fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen;)V
-}
-
-public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
- public fun ()V
- public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun getKey ()Ljava/lang/String;
- public final fun getPreferences ()Ljava/util/Set;
- public final fun getTitleKey ()Ljava/lang/String;
- public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
-}
-
-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/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;)V
- public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$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;
- public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen;
-}
-
-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/util/Set;)V
- public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;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;
-}
-
-public final class app/revanced/patches/shared/misc/settings/preference/InputType : java/lang/Enum {
- public static final field NUMBER Lapp/revanced/patches/shared/misc/settings/preference/InputType;
- public static final field TEXT Lapp/revanced/patches/shared/misc/settings/preference/InputType;
- public static final field TEXT_CAP_CHARACTERS Lapp/revanced/patches/shared/misc/settings/preference/InputType;
- public static final field TEXT_MULTI_LINE Lapp/revanced/patches/shared/misc/settings/preference/InputType;
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
- public final fun getType ()Ljava/lang/String;
- public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/InputType;
- public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/InputType;
-}
-
-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;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;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
- public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
-}
-
-public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent {
- public fun (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V
- public final fun copy (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
- public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
- public fun equals (Ljava/lang/Object;)Z
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final class app/revanced/patches/shared/misc/settings/preference/ListPreference : 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;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;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;)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 final fun getEntries ()Lapp/revanced/util/resource/ArrayResource;
- public final fun getEntriesKey ()Ljava/lang/String;
- public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource;
- public final fun getEntryValuesKey ()Ljava/lang/String;
- public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
-}
-
-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;Z)V
- public synthetic fun (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;Z)V
- public synthetic fun (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/util/Set;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;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/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreference {
- public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;Ljava/lang/String;Ljava/util/Set;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$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 final class app/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting : java/lang/Enum {
- public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
- public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
- public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
- public final fun getKeySuffix ()Ljava/lang/String;
- public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
- public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
-}
-
-public final class app/revanced/patches/shared/misc/settings/preference/SummaryType : java/lang/Enum {
- public static final field DEFAULT Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
- public static final field OFF Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
- public static final field ON Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
- public final fun getType ()Ljava/lang/String;
- public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
- public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
-}
-
-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;)V
- public synthetic fun (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;
-}
-
-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;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
- public synthetic fun (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/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/songpal/badge/BadgeTabPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field ACTIVITY_TAB_DESCRIPTOR Ljava/lang/String;
- public static final field INSTANCE Lapp/revanced/patches/songpal/badge/BadgeTabPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/songpal/badge/RemoveNotificationBadgePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/soundcloud/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/soundcloud/ad/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/soundcloud/analytics/DisableTelemetryPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/spotify/layout/theme/CustomThemePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/spotify/layout/theme/CustomThemePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/spotify/lite/ondemand/OnDemandPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/spotify/navbar/PremiumNavbarTabPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabResourcePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/spotify/navbar/PremiumNavbarTabResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/stocard/layout/HideOffersTabPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/stocard/layout/HideOffersTabPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/stocard/layout/HideStoryBubblesPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/strava/subscription/UnlockSubscriptionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheck : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheck;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/ticktick/misc/themeunlock/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/ticktick/misc/themeunlock/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/feedfilter/FeedFilterPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/feedfilter/FeedFilterPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/downloads/DownloadsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/misc/settings/SettingsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/settings/SettingsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/trakt/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/trakt/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/ads/DisableDashboardAds : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/ads/DisableDashboardAds;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/fixes/FixOldVersionsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/fixes/FixOldVersionsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/live/DisableTumblrLivePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/live/DisableTumblrLivePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/tumblr/timelinefilter/TimelineFilterPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/tumblr/timelinefilter/TimelineFilterPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitch/ad/audio/AudioAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/ad/audio/AudioAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public abstract class app/revanced/patches/twitch/ad/shared/util/BaseAdPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- protected final fun blockMethods (Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/String;[Ljava/lang/String;Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;)Z
- public static synthetic fun blockMethods$default (Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch;Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/String;[Ljava/lang/String;Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;ILjava/lang/Object;)Z
- protected final fun createConditionInstructions (Ljava/lang/String;)Ljava/lang/String;
- public static synthetic fun createConditionInstructions$default (Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;
- public final fun getConditionCall ()Ljava/lang/String;
- public final fun getSkipLabelName ()Ljava/lang/String;
-}
-
-protected final class app/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod {
- public fun ()V
- public fun (CLjava/lang/String;)V
- public synthetic fun (CLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()C
- public final fun component2 ()Ljava/lang/String;
- public final fun copy (CLjava/lang/String;)Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;
- public static synthetic fun copy$default (Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;CLjava/lang/String;ILjava/lang/Object;)Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getReturnType ()C
- public final fun getValue ()Ljava/lang/String;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final class app/revanced/patches/twitch/ad/video/VideoAdsPatch : app/revanced/patches/twitch/ad/shared/util/BaseAdPatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/ad/video/VideoAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitch/debug/DebugModePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/debug/DebugModePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitch/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/twitch/misc/settings/SettingsPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable {
- public static final field INSTANCE Lapp/revanced/patches/twitch/misc/settings/SettingsPatch;
- public fun close ()V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitch/misc/settings/SettingsResourcePatch : app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitch/misc/settings/SettingsResourcePatch;
-}
-
-public final class app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/layout/viewcount/HideViewCountPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/twitter/misc/hook/json/JsonHookPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable {
- public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/json/JsonHookPatch;
- public fun close ()V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public abstract class app/revanced/patches/twitter/misc/hook/patch/BaseHookPatch : app/revanced/patcher/patch/BytecodePatch {
- public fun (Ljava/lang/String;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitter/misc/hook/patch/ads/HideAdsHookPatch : app/revanced/patches/twitter/misc/hook/patch/BaseHookPatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/patch/ads/HideAdsHookPatch;
-}
-
-public final class app/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch : app/revanced/patches/twitter/misc/hook/patch/BaseHookPatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch;
-}
-
-public final class app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/vsco/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/vsco/misc/pro/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/willhaben/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/willhaben/ads/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/ad/general/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/ad/general/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/ad/general/HideAdsResourcePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/ad/general/HideAdsResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/ad/video/VideoAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/ad/video/VideoAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlBytecodePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/interaction/downloads/DownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/downloads/DownloadsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsBytecodePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/CustomBrandingPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/comments/CommentsPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/comments/CommentsPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfocardsResourcePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/infocards/HideInfocardsResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/loadmorebutton/HideLoadMoreButtonPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/loadmorebutton/HideLoadMoreButtonPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/time/HideTimestampPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsFingerprint;
-}
-
-public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/theme/ThemeBytecodePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictions : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictions;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/announcements/AnnouncementsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch : app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/check/CheckEnvironmentPatch;
-}
-
-public final class app/revanced/patches/youtube/misc/debugging/DebuggingPatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch;
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V
-}
-
-public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch;
-}
-
-public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/GmsCoreSupportResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook;
- public final fun addImageUrlErrorCallbackHook (Ljava/lang/String;)V
- public final fun addImageUrlHook (Ljava/lang/String;Z)V
- public static synthetic fun addImageUrlHook$default (Lapp/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook;Ljava/lang/String;ZILjava/lang/Object;)V
- public final fun addImageUrlSuccessCallbackHook (Ljava/lang/String;)V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/integrations/IntegrationsPatch;
-}
-
-public final class app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
- public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair;
- public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
- public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V
-}
-
-public final class app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch;
- public fun close ()V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public final fun getHookNavigationButtonCreated ()Lkotlin/jvm/functions/Function1;
-}
-
-public final class app/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch;
- public final fun addControls (Ljava/lang/String;)V
- public fun close ()V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public final fun initializeBottomControl (Ljava/lang/String;)V
- public final fun initializeControl (Ljava/lang/String;)V
- public final fun injectVisibilityCheckCall (Ljava/lang/String;)V
-}
-
-public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch;
- public final fun addBottomControls (Ljava/lang/String;)V
- public fun close ()V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/misc/settings/SettingsPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsPatch;
- public fun close ()V
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public final fun newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
-}
-
-public final class app/revanced/patches/youtube/misc/settings/SettingsPatch$PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsPatch$PreferenceScreen;
- public fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen;)V
- public final fun getADS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getALTERNATIVE_THUMBNAILS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getFEED ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getGENERAL_LAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getPLAYER ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getSEEKBAR ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getSHORTS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getSWIPE_CONTROLS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
- public final fun getVIDEO ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
-}
-
-public final class app/revanced/patches/youtube/misc/settings/SettingsResourcePatch : app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/hdrbrightness/HDRBrightnessPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/hdrbrightness/HDRBrightnessPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/information/VideoInformationPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/information/VideoInformationPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable, java/util/Set, kotlin/jvm/internal/markers/KMutableSet {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch;
- public fun add (Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch$Hook;)Z
- public synthetic fun add (Ljava/lang/Object;)Z
- public fun addAll (Ljava/util/Collection;)Z
- public fun clear ()V
- public fun close ()V
- public fun contains (Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch$Hook;)Z
- public final fun contains (Ljava/lang/Object;)Z
- public fun containsAll (Ljava/util/Collection;)Z
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun getSize ()I
- public fun isEmpty ()Z
- public fun iterator ()Ljava/util/Iterator;
- public fun remove (Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch$Hook;)Z
- public final fun remove (Ljava/lang/Object;)Z
- public fun removeAll (Ljava/util/Collection;)Z
- public fun retainAll (Ljava/util/Collection;)Z
- public final fun size ()I
- public fun toArray ()[Ljava/lang/Object;
- public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
-}
-
-public final class app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/quality/RememberVideoQualityPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/PlaybackSpeedPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/videoid/VideoIdPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/videoid/VideoIdPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public final fun hookBackgroundPlayVideoId (Ljava/lang/String;)V
- public final fun hookPlayerResponseVideoId (Ljava/lang/String;)V
- public final fun hookVideoId (Ljava/lang/String;)V
-}
-
-public final class app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch : app/revanced/patcher/patch/ResourcePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch;
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
- public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
-}
-
-public final class app/revanced/patches/youtubevanced/ad/general/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/youtubevanced/ad/general/HideAdsPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch : app/revanced/patcher/patch/BytecodePatch {
- public static final field INSTANCE Lapp/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch;
- public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
- public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
-}
-
-public final class app/revanced/util/BytecodeUtilsKt {
- public static final fun alsoResolve (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
- public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
- 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 findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
- public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
- public static final fun forEachLiteralValueInstruction (Lapp/revanced/patcher/data/BytecodeContext;JLkotlin/jvm/functions/Function2;)V
- public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException;
- 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
- public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
- public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
- public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
- public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;)I
- public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
- public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I
- public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
- public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
- public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I
- public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
- public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
- public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
- public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I
- public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
- public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
- public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
- public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
- public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
- public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
- public static final fun indexOfFirstWideLiteralInstructionValueReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
- public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
- public static final fun indexOfIdResourceOrThrow (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 resultOrThrow (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
- public static final fun returnEarly (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Z)V
- public static final fun returnEarly (Ljava/lang/Iterable;Z)V
- public static final fun returnEarly (Ljava/util/List;Z)V
- public static synthetic fun returnEarly$default (Lapp/revanced/patcher/fingerprint/MethodFingerprint;ZILjava/lang/Object;)V
- public static synthetic fun returnEarly$default (Ljava/lang/Iterable;ZILjava/lang/Object;)V
- public static synthetic fun returnEarly$default (Ljava/util/List;ZILjava/lang/Object;)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/data/BytecodeContext;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V
-}
-
-public final class app/revanced/util/ResourceGroup {
- public fun (Ljava/lang/String;[Ljava/lang/String;)V
- public final fun getResourceDirectoryName ()Ljava/lang/String;
- public final fun getResources ()[Ljava/lang/String;
-}
-
-public final class app/revanced/util/ResourceUtilsKt {
- public static final fun asSequence (Lorg/w3c/dom/NodeList;)Lkotlin/sequences/Sequence;
- public static final fun childElementsSequence (Lorg/w3c/dom/Node;)Lkotlin/sequences/Sequence;
- public static final fun copyResources (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;[Lapp/revanced/util/ResourceGroup;)V
- public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable;
- public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
- public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
- public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V
- public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
-}
-
-public abstract class app/revanced/util/patch/LiteralValueFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
- public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function0;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
-}
-
-public final class app/revanced/util/resource/ArrayResource : app/revanced/util/resource/BaseResource {
- public static final field Companion Lapp/revanced/util/resource/ArrayResource$Companion;
- public fun (Ljava/lang/String;Ljava/util/List;)V
- public final fun getItems ()Ljava/util/List;
- public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
-}
-
-public final class app/revanced/util/resource/ArrayResource$Companion {
- public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/ArrayResource;
-}
-
-public abstract class app/revanced/util/resource/BaseResource {
- public fun (Ljava/lang/String;Ljava/lang/String;)V
- public fun equals (Ljava/lang/Object;)Z
- public final fun getName ()Ljava/lang/String;
- public final fun getTag ()Ljava/lang/String;
- public fun hashCode ()I
- public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
- public static synthetic fun serialize$default (Lapp/revanced/util/resource/BaseResource;Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/w3c/dom/Element;
-}
-
-public final class app/revanced/util/resource/StringResource : app/revanced/util/resource/BaseResource {
- public static final field Companion Lapp/revanced/util/resource/StringResource$Companion;
- public fun (Ljava/lang/String;Ljava/lang/String;Z)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun getFormatted ()Z
- public final fun getValue ()Ljava/lang/String;
- public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
-}
-
-public final class app/revanced/util/resource/StringResource$Companion {
- public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/StringResource;
-}
-
diff --git a/build.gradle.kts b/build.gradle.kts
deleted file mode 100644
index 9ed141678..000000000
--- a/build.gradle.kts
+++ /dev/null
@@ -1,155 +0,0 @@
-import org.gradle.kotlin.dsl.support.listFilesOrdered
-import org.jetbrains.kotlin.gradle.dsl.JvmTarget
-
-plugins {
- alias(libs.plugins.kotlin)
- alias(libs.plugins.binary.compatibility.validator)
- `maven-publish`
- signing
-}
-
-group = "app.revanced"
-
-repositories {
- mavenCentral()
- mavenLocal()
- google()
- maven {
- // A repository must be specified for some reason. "registry" is a dummy.
- url = uri("https://maven.pkg.github.com/revanced/registry")
- credentials {
- username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
- password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
- }
- }
-}
-
-dependencies {
- implementation(libs.revanced.patcher)
- implementation(libs.smali)
- // TODO: Required because build fails without it. Find a way to remove this dependency.
- implementation(libs.guava)
- // Used in JsonGenerator.
- implementation(libs.gson)
- // Android API stubs defined here.
- compileOnly(project(":stub"))
-}
-
-kotlin {
- compilerOptions {
- jvmTarget.set(JvmTarget.JVM_11)
- }
-}
-
-java {
- targetCompatibility = JavaVersion.VERSION_11
-}
-
-tasks {
- withType(Jar::class) {
- exclude("app/revanced/meta")
-
- manifest {
- attributes["Name"] = "ReVanced Patches"
- attributes["Description"] = "Patches for ReVanced."
- attributes["Version"] = version
- attributes["Timestamp"] = System.currentTimeMillis().toString()
- attributes["Source"] = "git@github.com:revanced/revanced-patches.git"
- attributes["Author"] = "ReVanced"
- attributes["Contact"] = "contact@revanced.app"
- attributes["Origin"] = "https://revanced.app"
- attributes["License"] = "GNU General Public License v3.0"
- }
- }
-
- register("buildDexJar") {
- description = "Build and add a DEX to the JAR file"
- group = "build"
-
- dependsOn(build)
-
- doLast {
- val d8 = File(System.getenv("ANDROID_HOME")).resolve("build-tools")
- .listFilesOrdered().last().resolve("d8").absolutePath
-
- val patchesJar = configurations.archives.get().allArtifacts.files.files.first().absolutePath
- val workingDirectory = layout.buildDirectory.dir("libs").get().asFile
-
- exec {
- workingDir = workingDirectory
- commandLine = listOf(d8, "--release", patchesJar)
- }
-
- exec {
- workingDir = workingDirectory
- commandLine = listOf("zip", "-u", patchesJar, "classes.dex")
- }
- }
- }
-
- register("generatePatchesFiles") {
- description = "Generate patches files"
-
- dependsOn(build)
-
- classpath = sourceSets["main"].runtimeClasspath
- mainClass.set("app.revanced.generator.MainKt")
- }
-
- // Needed by gradle-semantic-release-plugin.
- // Tracking: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
- publish {
- dependsOn("buildDexJar")
- dependsOn("generatePatchesFiles")
- }
-}
-
-publishing {
- repositories {
- maven {
- name = "GitHubPackages"
- url = uri("https://maven.pkg.github.com/revanced/revanced-patches")
- credentials {
- username = System.getenv("GITHUB_ACTOR")
- password = System.getenv("GITHUB_TOKEN")
- }
- }
- }
-
- publications {
- create("revanced-patches-publication") {
- from(components["java"])
-
- pom {
- name = "ReVanced Patches"
- description = "Patches for ReVanced."
- url = "https://revanced.app"
-
- licenses {
- license {
- name = "GNU General Public License v3.0"
- url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
- }
- }
- developers {
- developer {
- id = "ReVanced"
- name = "ReVanced"
- email = "contact@revanced.app"
- }
- }
- scm {
- connection = "scm:git:git://github.com/revanced/revanced-patches.git"
- developerConnection = "scm:git:git@github.com:revanced/revanced-patches.git"
- url = "https://github.com/revanced/revanced-patches"
- }
- }
- }
- }
-}
-
-signing {
- useGpgCmd()
-
- sign(publishing.publications["revanced-patches-publication"])
-}
diff --git a/crowdin.yml b/crowdin.yml
index 4ac3cb98b..148f321cd 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -3,6 +3,6 @@ api_token_env: "CROWDIN_PERSONAL_TOKEN"
preserve_hierarchy: false
files:
- - source: src/main/resources/addresources/values/strings.xml
- translation: src/main/resources/addresources/values-%android_code%/strings.xml
+ - source: patches/src/main/resources/addresources/values/strings.xml
+ translation: patches/src/main/resources/addresources/values-%android_code%/strings.xml
skip_untranslated_strings: true
diff --git a/gradle.properties b/gradle.properties
index 5a96ec636..ce9413cb5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,5 @@
org.gradle.parallel = true
org.gradle.caching = true
+android.useAndroidX=true
kotlin.code.style = official
version = 4.18.0-dev.6
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 3a4060085..abf731bd3 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,18 +1,26 @@
[versions]
-revanced-patcher = "19.3.1"
+revanced-patcher = "20.0.2"
+# Tracking https://github.com/google/smali/issues/64.
#noinspection GradleDependency
-smali = "3.0.5" # 3.0.7 breaks binary compatibility. Tracking https://github.com/google/smali/issues/58.
-guava = "33.2.1-jre"
+smali = "3.0.5"
gson = "2.11.0"
-binary-compatibility-validator = "0.15.1"
-kotlin = "2.0.0"
+# 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818.
+#noinspection GradleDependency
+agp = "8.2.2"
+annotation = "1.9.0"
+appcompat = "1.7.0"
+okhttp = "5.0.0-alpha.14"
+retrofit = "2.11.0"
+guava = "33.2.1-jre"
[libraries]
-revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
-smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
-guava = { module = "com.google.guava:guava", version.ref = "guava" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
+annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
+appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
+retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
+guava = { module = "com.google.guava:guava", version.ref = "guava" }
+
[plugins]
-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
-kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+android-library = { id = "com.android.library", version.ref = "agp" }
diff --git a/patches/api/patches.api b/patches/api/patches.api
new file mode 100644
index 000000000..b7b1b9b29
--- /dev/null
+++ b/patches/api/patches.api
@@ -0,0 +1,1556 @@
+public final class app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatchKt {
+ public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+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 final class app/revanced/patches/all/misc/build/BuildInfo {
+ 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;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;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;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun getBoard ()Ljava/lang/String;
+ public final fun getBootloader ()Ljava/lang/String;
+ public final fun getBrand ()Ljava/lang/String;
+ public final fun getCpuAbi ()Ljava/lang/String;
+ public final fun getCpuAbi2 ()Ljava/lang/String;
+ public final fun getDevice ()Ljava/lang/String;
+ public final fun getDisplay ()Ljava/lang/String;
+ public final fun getFingerprint ()Ljava/lang/String;
+ public final fun getHardware ()Ljava/lang/String;
+ public final fun getHost ()Ljava/lang/String;
+ public final fun getId ()Ljava/lang/String;
+ public final fun getManufacturer ()Ljava/lang/String;
+ public final fun getModel ()Ljava/lang/String;
+ public final fun getOdmSku ()Ljava/lang/String;
+ public final fun getProduct ()Ljava/lang/String;
+ public final fun getRadio ()Ljava/lang/String;
+ public final fun getSerial ()Ljava/lang/String;
+ public final fun getSku ()Ljava/lang/String;
+ public final fun getSocManufacturer ()Ljava/lang/String;
+ public final fun getSocModel ()Ljava/lang/String;
+ public final fun getTags ()Ljava/lang/String;
+ public final fun getTime ()Ljava/lang/Long;
+ public final fun getType ()Ljava/lang/String;
+ public final fun getUser ()Ljava/lang/String;
+}
+
+public final class app/revanced/patches/all/misc/build/SpoofBuildInfoPatchKt {
+ public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatchKt {
+ public static final fun getSpoofWifiPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/all/misc/hex/HexPatchKt {
+ public static final fun getHexPatch ()Lapp/revanced/patcher/patch/RawResourcePatch;
+}
+
+public final class app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatchKt {
+ public static final fun getPredictiveBackGesturePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/all/misc/network/OverrideCertificatePinningPatchKt {
+ public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+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 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/resources/AddResourcesPatchKt {
+ public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
+ public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
+ public static final fun addResources (Ljava/lang/String;Ljava/lang/Iterable;)Z
+ public static final fun addResources (Ljava/lang/String;Ljava/lang/String;)V
+ public static final fun addResources (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Z
+ 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 final class app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatchKt {
+ public static final fun getRemoveScreenCaptureRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatchKt {
+ public static final fun getRemoveScreenshotRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatchKt {
+ public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall {
+ public abstract fun getDefinedClassName ()Ljava/lang/String;
+ 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 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 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 final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatchKt {
+ public static final fun getChangeVersionCodePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/amazon/DeepLinkingPatchKt {
+ public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt {
+ public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatchKt {
+ public static final fun getRemovePlayLimitsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatchKt {
+ public static final fun getBypassRootChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/duolingo/ad/DisableAdsPatchKt {
+ public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatchKt {
+ public static final fun getEnableDebugMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatchKt {
+ public static final fun getHideSponsoredStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/facebook/ads/story/HideStoryAdsPatchKt {
+ public static final fun getHideStoryAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatchKt {
+ public static final fun getBootloaderDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/finanzonline/detection/root/RootDetectionPatchKt {
+ public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatchKt {
+ public static final fun getEnableCustomTabsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/googlenews/misc/extension/ExtensionPatchKt {
+ public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatchKt {
+ public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/googlephotos/misc/extension/ExtensionPatchKt {
+ public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatchKt {
+ public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatchKt {
+ public static final fun getSpoofFeaturesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictionsKt {
+ public static final fun getRemoveDeviceRestrictionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/hexeditor/ad/DisableAdsPatchKt {
+ public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatchKt {
+ public static final fun getUnlockProPatch ()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 final class app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatchKt {
+ public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/inshorts/ad/InshortsAdsPatchKt {
+ public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/instagram/ads/HideAdsPatchKt {
+ public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt {
+ public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
+ public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatchKt {
+ public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/memegenerator/detection/license/LicenseValidationPatchKt {
+ public static final fun getLicenseValidationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatchKt {
+ public static final fun getSignatureVerificationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatchKt {
+ public static final fun getUnlockProVersionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/messenger/inbox/HideInboxAdsPatchKt {
+ public static final fun getHideInboxAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/messenger/inbox/HideInboxSubtabsPatchKt {
+ public static final fun getHideInboxSubtabsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatchKt {
+ public static final fun getDisableSwitchingEmojiToStickerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatchKt {
+ public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt {
+ public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/mifitness/misc/login/FixLoginPatchKt {
+ public static final fun getFixLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/music/ad/video/HideVideoAdsKt {
+ public static final fun getHideVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlaybackKt {
+ public static final fun getEnableExclusiveAudioPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/music/layout/compactheader/HideCategoryBarKt {
+ public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/music/layout/premium/HideGetPremiumPatchKt {
+ public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatchKt {
+ public static final fun getRemoveUpgradeButtonPatch ()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 final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt {
+ public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/music/misc/gms/Constants {
+ public static final field INSTANCE Lapp/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 final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
+ public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/myfitnesspal/ads/HideAdsPatchKt {
+ public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt {
+ public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatchKt {
+ public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt {
+ public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/photomath/detection/signature/SignatureDetectionPatchKt {
+ public static final fun getSignatureDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatchKt {
+ public static final fun getHideUpdatePopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatchKt {
+ public static final fun getEnableBookpointPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatchKt {
+ public static final fun getUnlockPlusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatchKt {
+ public static final fun getSpoofAndroidDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/piccomafr/tracking/DisableTrackingPatchKt {
+ public static final fun getDisableTrackingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt {
+ public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/reddit/ad/comments/HideCommentAdsPatchKt {
+ public static final fun getHideCommentAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/ad/general/HideAdsPatchKt {
+ public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 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 final class app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatchKt {
+ public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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/slink/FixSLinksPatchKt {
+ public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
+ public static final fun getFixSlinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatchKt {
+ public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatchKt {
+ public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatchKt {
+ public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/redditisfun/api/FingerprintsKt {
+ public static final fun baseClientIdFingerprint (Ljava/lang/String;)Lapp/revanced/patcher/Fingerprint;
+}
+
+public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatchKt {
+ public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatchKt {
+ public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatchKt {
+ public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatchKt {
+ public static final fun getDisableSyncForLemmyBottomSheetPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatchKt {
+ public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatchKt {
+ public static final fun getUseUserEndpointPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatchKt {
+ public static final fun getFixVideoDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatchKt {
+ public static final fun getDisableScreenshotPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatchKt {
+ public static final fun getUnlockPremiumIconPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/misc/extension/ExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatchKt {
+ public static final fun getSanitizeUrlQueryPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatchKt {
+ public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/shared/misc/extension/ExtensionHook {
+ public final fun getFingerprint ()Lapp/revanced/patcher/Fingerprint;
+ public final fun invoke (Ljava/lang/String;)V
+}
+
+public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt {
+ public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
+ public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
+ public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatchKt {
+ public static final fun getVerticalScrollPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/shared/misc/gms/FingerprintsKt {
+ public static final field GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME Ljava/lang/String;
+}
+
+public final class app/revanced/patches/shared/misc/gms/GmsCoreSupportPatchKt {
+ public static final fun gmsCoreSupportPatch (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/Fingerprint;Ljava/util/Set;Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
+ public static synthetic fun gmsCoreSupportPatch$default (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/Fingerprint;Ljava/util/Set;Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;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/Function2;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/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/shared/misc/hex/HexPatchKt {
+ public static final fun hexPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/RawResourcePatch;
+}
+
+public final class app/revanced/patches/shared/misc/hex/Replacement {
+ public static final field Companion Lapp/revanced/patches/shared/misc/hex/Replacement$Companion;
+ public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ public final fun replacePattern ([B)V
+}
+
+public final class app/revanced/patches/shared/misc/hex/Replacement$Companion {
+}
+
+public final class app/revanced/patches/shared/misc/mapping/ResourceElement {
+ public final fun component1 ()Ljava/lang/String;
+ 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 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 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 final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
+ public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
+ public static synthetic fun settingsPatch$default (Lkotlin/Pair;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference {
+ public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;
+ public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getKey ()Ljava/lang/String;
+ public final fun getSummaryKey ()Ljava/lang/String;
+ public final fun getTag ()Ljava/lang/String;
+ public final fun getTitleKey ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
+}
+
+public final class app/revanced/patches/shared/misc/settings/preference/BasePreference$Companion {
+ public final fun addSummary (Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;)V
+ public static synthetic fun addSummary$default (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;ILjava/lang/Object;)V
+}
+
+public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen : java/io/Closeable {
+ public fun ()V
+ public fun (Ljava/util/Set;)V
+ public synthetic fun (Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun close ()V
+ public abstract fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference;)V
+}
+
+public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
+ public fun ()V
+ public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun getKey ()Ljava/lang/String;
+ public final fun getPreferences ()Ljava/util/Set;
+ public final fun getTitleKey ()Ljava/lang/String;
+ public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
+}
+
+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/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/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;
+ public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference;
+}
+
+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/util/Set;)V
+ public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;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;
+}
+
+public final class app/revanced/patches/shared/misc/settings/preference/InputType : java/lang/Enum {
+ public static final field NUMBER Lapp/revanced/patches/shared/misc/settings/preference/InputType;
+ public static final field TEXT Lapp/revanced/patches/shared/misc/settings/preference/InputType;
+ public static final field TEXT_CAP_CHARACTERS Lapp/revanced/patches/shared/misc/settings/preference/InputType;
+ public static final field TEXT_MULTI_LINE Lapp/revanced/patches/shared/misc/settings/preference/InputType;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public final fun getType ()Ljava/lang/String;
+ public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/InputType;
+ public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/InputType;
+}
+
+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;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;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
+ public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
+}
+
+public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent {
+ public fun (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V
+ public final fun copy (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
+ public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class app/revanced/patches/shared/misc/settings/preference/ListPreference : 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;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;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;)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 final fun getEntries ()Lapp/revanced/util/resource/ArrayResource;
+ public final fun getEntriesKey ()Ljava/lang/String;
+ public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource;
+ public final fun getEntryValuesKey ()Ljava/lang/String;
+ public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
+}
+
+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;Z)V
+ public synthetic fun (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/util/Set;)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/String;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;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;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 final class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting : java/lang/Enum {
+ public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
+ public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
+ public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public final fun getKeySuffix ()Ljava/lang/String;
+ public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
+ public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
+}
+
+public final class app/revanced/patches/shared/misc/settings/preference/SummaryType : java/lang/Enum {
+ public static final field DEFAULT Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
+ public static final field OFF Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
+ public static final field ON Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public final fun getType ()Ljava/lang/String;
+ public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
+ public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
+}
+
+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;)V
+ public synthetic fun (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;
+}
+
+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;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
+ public synthetic fun (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/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
+ public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/songpal/badge/BadgeTabPatchKt {
+ public static final fun getBadgeTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/songpal/badge/RemoveNotificationBadgePatchKt {
+ public static final fun getRemoveNotificationBadgePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/soundcloud/ad/HideAdsPatchKt {
+ public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPatchKt {
+ public static final fun getDisableTelemetryPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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/theme/CustomThemePatchKt {
+ public static final fun getCustomThemePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatchKt {
+ public static final fun getOnDemandPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
+ public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/stocard/layout/HideOffersTabPatchKt {
+ public static final fun getHideOffersTabPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatchKt {
+ public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+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 final class app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatchKt {
+ public static final fun getRemoveGooglePlayIntegrityCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatchKt {
+ public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/feedfilter/FeedFilterPatchKt {
+ public static final fun getFeedFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatchKt {
+ public static final fun getRememberClearDisplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/interaction/downloads/DownloadsPatchKt {
+ public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatchKt {
+ public static final fun getShowSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatchKt {
+ public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/misc/extension/ExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatchKt {
+ public static final fun getDisableLoginRequirementPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatchKt {
+ public static final fun getFixGoogleLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/misc/settings/SettingsPatchKt {
+ public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatchKt {
+ public static final fun getSpoofSimPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/trakt/UnlockProPatchKt {
+ public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatchKt {
+ public static final fun getShowOnLockscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tudortmund/misc/extension/ExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/ads/DisableDashboardAdsKt {
+ public static final fun getDisableDashboardAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatchKt {
+ public static final fun getDisableAdFreeBannerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatchKt {
+ public static final fun getDisableInAppUpdatePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatchKt {
+ public static final fun getDisableBlogNotificationReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatchKt {
+ public static final fun getDisableGiftMessagePopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatchKt {
+ public static final fun getOverrideFeatureFlagsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/fixes/FixOldVersionsPatchKt {
+ public static final fun getFixOldVersionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/live/DisableTumblrLivePatchKt {
+ public static final fun getDisableTumblrLivePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/tumblr/misc/extension/ExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 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 final class app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatchKt {
+ public static final fun getEmbeddedAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/twitch/ad/shared/util/ReturnMethod {
+ public static final field Companion Lapp/revanced/patches/twitch/ad/shared/util/ReturnMethod$Companion;
+ public fun (CLjava/lang/String;)V
+ public final fun getReturnType ()C
+ public final fun getValue ()Ljava/lang/String;
+}
+
+public final class app/revanced/patches/twitch/ad/shared/util/ReturnMethod$Companion {
+ public final fun getDefault ()Lapp/revanced/patches/twitch/ad/shared/util/ReturnMethod;
+}
+
+public final class app/revanced/patches/twitch/ad/video/VideoAdsPatchKt {
+ public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatchKt {
+ public static final fun getShowDeletedMessagesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatchKt {
+ public static final fun getAutoClaimChannelPointsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitch/debug/DebugModePatchKt {
+ public static final fun getDebugModePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitch/misc/extension/SharedExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatchKt {
+ public static final fun getUnlockDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitter/layout/viewcount/HideViewCountPatchKt {
+ public static final fun getHideViewCountPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatchKt {
+ public static final fun getDynamicColorPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/twitter/misc/extension/ExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitter/misc/hook/HideAdsHookPatchKt {
+ public static final fun getHideAdsHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/twitter/misc/hook/HideRecommendedUsersPatchKt {
+ public static final fun getHideRecommendedUsersPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 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/JsonHookPatchHook : java/io/Closeable {
+ public fun (Lapp/revanced/patcher/Fingerprint;)V
+ public final fun addHook (Lapp/revanced/patches/twitter/misc/hook/json/JsonHook;)V
+ public fun close ()V
+}
+
+public final class app/revanced/patches/twitter/misc/hook/json/JsonHookPatchKt {
+ public static final fun getJsonHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatchKt {
+ public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/vsco/misc/pro/UnlockProPatchKt {
+ public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatchKt {
+ public static final fun getFirebaseGetCertPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatchKt {
+ public static final fun getPromoCodeUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatchKt {
+ public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/ad/general/HideAdsPatchKt {
+ public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatchKt {
+ public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/ad/video/VideoAdsPatchKt {
+ public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatchKt {
+ public static final fun getCopyVideoUrlPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatchKt {
+ public static final fun getRemoveViewerDiscretionDialogPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/interaction/downloads/DownloadsPatchKt {
+ public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatchKt {
+ public static final fun getDisablePreciseSeekingGesturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatchKt {
+ public static final fun getEnableSeekbarTappingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatchKt {
+ public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatchKt {
+ public static final fun getSwipeControlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatchKt {
+ public static final fun getAutoCaptionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPatchKt {
+ public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatchKt {
+ public static final fun getChangeHeaderPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatchKt {
+ public static final fun getHideButtonsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatchKt {
+ public static final fun getNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatchKt {
+ public static final fun getHidePlayerOverlayButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatchKt {
+ public static final fun getBreakingNewsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatchKt {
+ public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatchKt {
+ public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatchKt {
+ public static final fun getAlbumCardId ()J
+ public static final fun getBarContainerHeightId ()J
+ public static final fun getCrowdfundingBoxId ()J
+ public static final fun getExpandButtonDownId ()J
+ public static final fun getFabButtonId ()J
+ public static final fun getFilterBarHeightId ()J
+ public static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+ public static final fun getRelatedChipCloudMarginId ()J
+ public static final fun getYouTubeLogo ()J
+}
+
+public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatchKt {
+ public static final fun getHideInfoCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatchKt {
+ public static final fun getHidePlayerFlyoutMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatchKt {
+ public static final fun getHideShortsComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatchKt {
+ public static final fun getDisableSuggestedVideoEndScreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPatchKt {
+ public static final fun getHideTimestampPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/miniplayer/FingerprintsKt {
+ public static final field ANIMATION_INTERPOLATION_FEATURE_KEY J
+ public static final field DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL J
+ public static final field DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL J
+ public static final field DROP_SHADOW_FEATURE_KEY J
+ public static final field INITIAL_SIZE_FEATURE_KEY_LITERAL J
+ public static final field MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL J
+ public static final field MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY J
+ public static final field ROUNDED_CORNERS_FEATURE_KEY J
+}
+
+public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatchKt {
+ public static final fun getFloatyBarButtonTopMargin ()J
+ public static final fun getMiniplayerMaxSize ()J
+ public static final fun getMiniplayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+ public static final fun getModernMiniplayerClose ()J
+ public static final fun getModernMiniplayerExpand ()J
+ public static final fun getModernMiniplayerForwardButton ()J
+ public static final fun getModernMiniplayerRewindButton ()J
+ public static final fun getPlayerOverlays ()J
+ public static final fun getScrimOverlay ()J
+ public static final fun getYtOutlinePictureInPictureWhite24 ()J
+ public static final fun getYtOutlineXWhite24 ()J
+}
+
+public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatchKt {
+ public static final fun getPlayerPopupPanelsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatchKt {
+ public static final fun getPlayerControlsBackgroundPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatchKt {
+ public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatchKt {
+ public static final fun getReturnYouTubeDislikePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/returnyoutubedislike/Vote : java/lang/Enum {
+ public static final field DISLIKE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote;
+ public static final field LIKE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote;
+ public static final field REMOVE_LIKE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public final fun getValue ()I
+ public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote;
+ public static fun values ()[Lapp/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 final class app/revanced/patches/youtube/layout/seekbar/FingerprintsKt {
+ public static final field PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG J
+}
+
+public final class app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatchKt {
+ public static final fun getRestoreOldSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatchKt {
+ public static final fun getSeekbarColorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatchKt {
+ public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
+ public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatchKt {
+ public static final fun getSpoofAppVersionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatchKt {
+ public static final fun getChangeStartPagePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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/startupshortsreset/FingerprintsKt {
+ public static final fun indexOfOptionalInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I
+}
+
+public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatchKt {
+ public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
+ public static final fun getEnableTabletLayoutPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/theme/LithoColorHookPatchKt {
+ public static final fun getLithoColorHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+ public static final fun getLithoColorOverrideHook ()Lkotlin/jvm/functions/Function2;
+}
+
+public final class app/revanced/patches/youtube/layout/theme/ThemePatchKt {
+ public static final fun getThemePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatchKt {
+ public static final fun getAlternativeThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatchKt {
+ public static final fun getBypassImageRegionRestrictionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/announcements/AnnouncementsPatchKt {
+ public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt {
+ public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatchKt {
+ public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatchKt {
+ public static final fun getCheckEnvironmentPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt {
+ public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatchKt {
+ public static final fun getSpoofDeviceDimensionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatchKt {
+ public static final fun getCheckWatchHistoryDomainNameResolutionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/extension/SharedExtensionPatchKt {
+ public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/fix/cairo/FingerprintsKt {
+ public static final field CAIRO_CONFIG_LITERAL_VALUE J
+}
+
+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/gms/GmsCoreSupportPatchKt {
+ public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHookKt {
+ public static final fun addImageUrlErrorCallbackHook (Ljava/lang/String;)V
+ 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 final class app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatchKt {
+ public static final fun getBypassURLRedirectsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatchKt {
+ public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 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 setHookNavigationButtonCreated (Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatchKt {
+ public static final fun getAddBottomControl ()Lkotlin/jvm/functions/Function1;
+ public static final fun getPlayerControlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+ public static final fun getPlayerControlsResourcePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+ public static final fun initializeBottomControl (Ljava/lang/String;)V
+ public static final fun injectVisibilityCheckCall (Ljava/lang/String;)V
+}
+
+public final class app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatchKt {
+ public static final fun getPlayerTypeHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPatchKt {
+ public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
+ public static final fun is_19_03_or_greater ()Z
+ public static final fun is_19_04_or_greater ()Z
+ public static final fun is_19_16_or_greater ()Z
+ public static final fun is_19_17_or_greater ()Z
+ public static final fun is_19_18_or_greater ()Z
+ public static final fun is_19_23_or_greater ()Z
+ public static final fun is_19_25_or_greater ()Z
+ public static final fun is_19_26_or_greater ()Z
+ public static final fun is_19_29_or_greater ()Z
+ public static final fun is_19_32_or_greater ()Z
+ public static final fun is_19_33_or_greater ()Z
+ public static final fun is_19_34_or_greater ()Z
+ public static final fun is_19_36_or_greater ()Z
+ public static final fun is_19_41_or_greater ()Z
+ public static final fun is_19_43_or_greater ()Z
+}
+
+public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
+ public static final fun getRemoveTrackingQueryParameterPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+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 final class app/revanced/patches/youtube/misc/settings/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen {
+ public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/PreferenceScreen;
+ public fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference;)V
+ public final fun getADS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getALTERNATIVE_THUMBNAILS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getFEED ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getGENERAL_LAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getPLAYER ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getSEEKBAR ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getSHORTS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getSWIPE_CONTROLS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+ public final fun getVIDEO ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
+}
+
+public final class app/revanced/patches/youtube/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 newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
+}
+
+public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatchKt {
+ public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/shared/FingerprintsKt {
+ public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
+}
+
+public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
+ public static final fun getSetPlaybackSpeedClassFieldReference ()Ljava/lang/String;
+ public static final fun getSetPlaybackSpeedContainerClassFieldReference ()Ljava/lang/String;
+ public static final fun getSetPlaybackSpeedMethodReference ()Ljava/lang/String;
+ public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+ public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V
+ public static final fun videoTimeHook (Ljava/lang/String;Ljava/lang/String;)V
+}
+
+public abstract class app/revanced/patches/youtube/video/playerresponse/Hook {
+ public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class app/revanced/patches/youtube/video/playerresponse/Hook$ProtoBufferParameter : app/revanced/patches/youtube/video/playerresponse/Hook {
+ public fun (Ljava/lang/String;)V
+}
+
+public final class app/revanced/patches/youtube/video/playerresponse/Hook$ProtoBufferParameterBeforeVideoId : app/revanced/patches/youtube/video/playerresponse/Hook {
+ public fun (Ljava/lang/String;)V
+}
+
+public final class app/revanced/patches/youtube/video/playerresponse/Hook$VideoId : app/revanced/patches/youtube/video/playerresponse/Hook {
+ public fun (Ljava/lang/String;)V
+}
+
+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 final class app/revanced/patches/youtube/video/quality/RememberVideoQualityPatchKt {
+ public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt {
+ public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatchKt {
+ public static final fun getPlaybackSpeedButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
+public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatchKt {
+ public static final fun getSpeedUnavailableId ()J
+}
+
+public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt {
+ public static final fun getVideoIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+ 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 applyMatch (Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/patcher/Match;)Lapp/revanced/patcher/Match;
+ public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
+ 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 findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
+ public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
+ public static final fun forEachLiteralValueInstruction (Lapp/revanced/patcher/patch/BytecodePatchContext;JLkotlin/jvm/functions/Function2;)V
+ public static final fun getException (Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patcher/patch/PatchException;
+ public static final fun getMatchOrThrow (Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patcher/Match;
+ 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
+ public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
+ public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
+ public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
+ public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;)I
+ public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
+ public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I
+ public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
+ public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
+ public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I
+ public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
+ public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
+ public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
+ public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I
+ public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
+ public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
+ public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
+ public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
+ public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
+ public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
+ public static final fun indexOfFirstWideLiteralInstructionValueReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
+ public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
+ public static final fun indexOfIdResourceOrThrow (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/Fingerprint;Z)V
+ public static final fun returnEarly (Ljava/lang/Iterable;Z)V
+ public static final fun returnEarly (Ljava/util/List;Z)V
+ public static synthetic fun returnEarly$default (Lapp/revanced/patcher/Fingerprint;ZILjava/lang/Object;)V
+ public static synthetic fun returnEarly$default (Ljava/lang/Iterable;ZILjava/lang/Object;)V
+ public static synthetic fun returnEarly$default (Ljava/util/List;ZILjava/lang/Object;)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 final class app/revanced/util/ResourceGroup {
+ public fun (Ljava/lang/String;[Ljava/lang/String;)V
+ public final fun getResourceDirectoryName ()Ljava/lang/String;
+ public final fun getResources ()[Ljava/lang/String;
+}
+
+public final class app/revanced/util/ResourceUtilsKt {
+ public static final fun asSequence (Lorg/w3c/dom/NodeList;)Lkotlin/sequences/Sequence;
+ public static final fun childElementsSequence (Lorg/w3c/dom/Node;)Lkotlin/sequences/Sequence;
+ public static final fun copyResources (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;[Lapp/revanced/util/ResourceGroup;)V
+ public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/Document;Lapp/revanced/patcher/util/Document;)Ljava/lang/AutoCloseable;
+ public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
+ public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
+ public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V
+ public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class app/revanced/util/resource/ArrayResource : app/revanced/util/resource/BaseResource {
+ public static final field Companion Lapp/revanced/util/resource/ArrayResource$Companion;
+ public fun (Ljava/lang/String;Ljava/util/List;)V
+ public final fun getItems ()Ljava/util/List;
+ public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
+}
+
+public final class app/revanced/util/resource/ArrayResource$Companion {
+ public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/ArrayResource;
+}
+
+public abstract class app/revanced/util/resource/BaseResource {
+ public fun (Ljava/lang/String;Ljava/lang/String;)V
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getName ()Ljava/lang/String;
+ public final fun getTag ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
+ public static synthetic fun serialize$default (Lapp/revanced/util/resource/BaseResource;Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/w3c/dom/Element;
+}
+
+public final class app/revanced/util/resource/StringResource : app/revanced/util/resource/BaseResource {
+ public static final field Companion Lapp/revanced/util/resource/StringResource$Companion;
+ public fun (Ljava/lang/String;Ljava/lang/String;Z)V
+ public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun getFormatted ()Z
+ public final fun getValue ()Ljava/lang/String;
+ public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
+}
+
+public final class app/revanced/util/resource/StringResource$Companion {
+ public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/StringResource;
+}
+
diff --git a/patches/build.gradle.kts b/patches/build.gradle.kts
new file mode 100644
index 000000000..dab82da23
--- /dev/null
+++ b/patches/build.gradle.kts
@@ -0,0 +1,35 @@
+group = "app.revanced"
+
+patches {
+ about {
+ name = "ReVanced Patches"
+ description = "Patches for ReVanced"
+ source = "git@github.com:revanced/revanced-patches.git"
+ author = "ReVanced"
+ contact = "contact@revanced.app"
+ website = "https://revanced.app"
+ license = "GNU General Public License v3.0"
+ }
+}
+
+dependencies {
+ // Used by JsonGenerator.
+ implementation(libs.gson)
+ // Required due to smali, or build fails. Can be removed once smali is bumped.
+ implementation(libs.guava)
+ // Android API stubs defined here.
+ compileOnly(project(":patches:stub"))
+}
+
+publishing {
+ repositories {
+ maven {
+ name = "GitHubPackages"
+ url = uri("https://maven.pkg.github.com/revanced/revanced-patches")
+ credentials {
+ username = System.getenv("GITHUB_ACTOR")
+ password = System.getenv("GITHUB_TOKEN")
+ }
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt
similarity index 63%
rename from src/main/kotlin/app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt
index ccb324694..35c7b24f7 100644
--- a/src/main/kotlin/app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt
@@ -1,27 +1,22 @@
-package app.revanced.patches.all.activity.exportall
+package app.revanced.patches.all.misc.activity.exportall
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.resourcePatch
-@Patch(
+@Suppress("unused")
+val exportAllActivitiesPatch = resourcePatch(
name = "Export all activities",
description = "Makes all app activities exportable.",
use = false,
-)
-@Suppress("unused")
-object ExportAllActivitiesPatch : ResourcePatch() {
- private const val EXPORTED_FLAG = "android:exported"
-
- override fun execute(context: ResourceContext) {
- context.xmlEditor["AndroidManifest.xml"].use { editor ->
- val document = editor.file
+) {
+ execute { context ->
+ val exportedFlag = "android:exported"
+ context.document["AndroidManifest.xml"].use { document ->
val activities = document.getElementsByTagName("activity")
for (i in 0..activities.length) {
activities.item(i)?.apply {
- val exportedAttribute = attributes.getNamedItem(EXPORTED_FLAG)
+ val exportedAttribute = attributes.getNamedItem(exportedFlag)
if (exportedAttribute != null) {
if (exportedAttribute.nodeValue != "true") {
@@ -31,7 +26,7 @@ object ExportAllActivitiesPatch : ResourcePatch() {
// Reason why the attribute is added in the case it does not exist:
// https://github.com/revanced/revanced-patches/pull/1751/files#r1141481604
else {
- document.createAttribute(EXPORTED_FLAG)
+ document.createAttribute(exportedFlag)
.apply { value = "true" }
.let(attributes::setNamedItem)
}
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
new file mode 100644
index 000000000..434b97e27
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt
@@ -0,0 +1,92 @@
+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.patch.bytecodePatch
+import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
+import app.revanced.util.getReference
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+import com.android.tools.smali.dexlib2.iface.reference.FieldReference
+
+private const val BUILD_CLASS_DESCRIPTOR = "Landroid/os/Build;"
+
+class BuildInfo(
+ // The build information supported32BitAbis, supported64BitAbis, and supportedAbis are not supported for now,
+ // because initializing an array in transform is a bit more complex.
+ val board: String? = null,
+ val bootloader: String? = null,
+ val brand: String? = null,
+ val cpuAbi: String? = null,
+ val cpuAbi2: String? = null,
+ val device: String? = null,
+ val display: String? = null,
+ val fingerprint: String? = null,
+ val hardware: String? = null,
+ val host: String? = null,
+ val id: String? = null,
+ val manufacturer: String? = null,
+ val model: String? = null,
+ val odmSku: String? = null,
+ val product: String? = null,
+ val radio: String? = null,
+ val serial: String? = null,
+ val sku: String? = null,
+ val socManufacturer: String? = null,
+ val socModel: String? = null,
+ val tags: String? = null,
+ val time: Long? = null,
+ val type: String? = null,
+ val user: String? = null,
+)
+
+fun baseSpoofBuildInfoPatch(buildInfoSupplier: () -> BuildInfo) = bytecodePatch {
+ // Lazy, so that patch options above are initialized before they are accessed.
+ val replacements by lazy {
+ with(buildInfoSupplier()) {
+ buildMap {
+ if (board != null) put("BOARD", "const-string" to "\"$board\"")
+ if (bootloader != null) put("BOOTLOADER", "const-string" to "\"$bootloader\"")
+ if (brand != null) put("BRAND", "const-string" to "\"$brand\"")
+ if (cpuAbi != null) put("CPU_ABI", "const-string" to "\"$cpuAbi\"")
+ if (cpuAbi2 != null) put("CPU_ABI2", "const-string" to "\"$cpuAbi2\"")
+ if (device != null) put("DEVICE", "const-string" to "\"$device\"")
+ if (display != null) put("DISPLAY", "const-string" to "\"$display\"")
+ if (fingerprint != null) put("FINGERPRINT", "const-string" to "\"$fingerprint\"")
+ if (hardware != null) put("HARDWARE", "const-string" to "\"$hardware\"")
+ if (host != null) put("HOST", "const-string" to "\"$host\"")
+ if (id != null) put("ID", "const-string" to "\"$id\"")
+ if (manufacturer != null) put("MANUFACTURER", "const-string" to "\"$manufacturer\"")
+ if (model != null) put("MODEL", "const-string" to "\"$model\"")
+ if (odmSku != null) put("ODM_SKU", "const-string" to "\"$odmSku\"")
+ if (product != null) put("PRODUCT", "const-string" to "\"$product\"")
+ if (radio != null) put("RADIO", "const-string" to "\"$radio\"")
+ if (serial != null) put("SERIAL", "const-string" to "\"$serial\"")
+ if (sku != null) put("SKU", "const-string" to "\"$sku\"")
+ if (socManufacturer != null) put("SOC_MANUFACTURER", "const-string" to "\"$socManufacturer\"")
+ if (socModel != null) put("SOC_MODEL", "const-string" to "\"$socModel\"")
+ if (tags != null) put("TAGS", "const-string" to "\"$tags\"")
+ if (time != null) put("TIME", "const-wide" to "$time")
+ if (type != null) put("TYPE", "const-string" to "\"$type\"")
+ if (user != null) put("USER", "const-string" to "\"$user\"")
+ }
+ }
+ }
+
+ dependsOn(
+ transformInstructionsPatch(
+ filterMap = filterMap@{ _, _, instruction, instructionIndex ->
+ val reference = instruction.getReference() ?: return@filterMap null
+ if (reference.definingClass != BUILD_CLASS_DESCRIPTOR) return@filterMap null
+
+ return@filterMap replacements[reference.name]?.let { instructionIndex to it }
+ },
+ transform = { mutableMethod, entry ->
+ val (index, replacement) = entry
+ val (opcode, operand) = replacement
+ val register = mutableMethod.getInstruction(index).registerA
+
+ mutableMethod.replaceInstruction(index, "$opcode v$register, $operand")
+ },
+ ),
+ )
+}
diff --git a/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt
similarity index 57%
rename from src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt
index 994ddd455..7cfd385c6 100644
--- a/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt
@@ -1,183 +1,214 @@
package app.revanced.patches.all.misc.build
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.longPatchOption
-import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patcher.patch.longOption
+import app.revanced.patcher.patch.stringOption
-@Patch(
+@Suppress("unused")
+val spoofBuildInfoPatch = bytecodePatch(
name = "Spoof build info",
description = "Spoof the information about the current build.",
- use = false
-)
-@Suppress("unused")
-class SpoofBuildInfoPatch : BaseSpoofBuildInfoPatch() {
- override val board by stringPatchOption(
+ use = false,
+) {
+ val board by stringOption(
key = "board",
default = null,
title = "Board",
- description = "The name of the underlying board, like \"goldfish\"."
+ description = "The name of the underlying board, like \"goldfish\".",
)
- override val bootloader by stringPatchOption(
+ val bootloader by stringOption(
key = "bootloader",
default = null,
title = "Bootloader",
- description = "The system bootloader version number."
+ description = "The system bootloader version number.",
)
- override val brand by stringPatchOption(
+ val brand by stringOption(
key = "brand",
default = null,
title = "Brand",
- description = "The consumer-visible brand with which the product/hardware will be associated, if any."
+ description = "The consumer-visible brand with which the product/hardware will be associated, if any.",
)
- override val cpuAbi by stringPatchOption(
+ val cpuAbi by stringOption(
key = "cpu-abi",
default = null,
title = "CPU ABI",
- description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead."
+ description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.",
)
- override val cpuAbi2 by stringPatchOption(
+ val cpuAbi2 by stringOption(
key = "cpu-abi-2",
default = null,
title = "CPU ABI 2",
- description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead."
+ description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.",
)
- override val device by stringPatchOption(
+ val device by stringOption(
key = "device",
default = null,
title = "Device",
- description = "The name of the industrial design."
+ description = "The name of the industrial design.",
)
- override val display by stringPatchOption(
+ val display by stringOption(
key = "display",
default = null,
title = "Display",
- description = "A build ID string meant for displaying to the user."
+ description = "A build ID string meant for displaying to the user.",
)
- override val fingerprint by stringPatchOption(
+ val fingerprint by stringOption(
key = "fingerprint",
default = null,
title = "Fingerprint",
- description = "A string that uniquely identifies this build."
+ description = "A string that uniquely identifies this build.",
)
- override val hardware by stringPatchOption(
+ val hardware by stringOption(
key = "hardware",
default = null,
title = "Hardware",
- description = "The name of the hardware (from the kernel command line or /proc)."
+ description = "The name of the hardware (from the kernel command line or /proc).",
)
- override val host by stringPatchOption(
+ val host by stringOption(
key = "host",
default = null,
title = "Host",
- description = "The host."
+ description = "The host.",
)
- override val id by stringPatchOption(
+ val id by stringOption(
key = "id",
default = null,
title = "ID",
- description = "Either a changelist number, or a label like \"M4-rc20\"."
+ description = "Either a changelist number, or a label like \"M4-rc20\".",
)
- override val manufacturer by stringPatchOption(
+ val manufacturer by stringOption(
key = "manufacturer",
default = null,
title = "Manufacturer",
- description = "The manufacturer of the product/hardware."
+ description = "The manufacturer of the product/hardware.",
)
- override val model by stringPatchOption(
+ val model by stringOption(
key = "model",
default = null,
title = "Model",
- description = "The end-user-visible name for the end product."
+ description = "The end-user-visible name for the end product.",
)
- override val odmSku by stringPatchOption(
+ val odmSku by stringOption(
key = "odm-sku",
default = null,
title = "ODM SKU",
- description = "The SKU of the device as set by the original design manufacturer (ODM)."
+ description = "The SKU of the device as set by the original design manufacturer (ODM).",
)
- override val product by stringPatchOption(
+ val product by stringOption(
key = "product",
default = null,
title = "Product",
- description = "The name of the overall product."
+ description = "The name of the overall product.",
)
- override val radio by stringPatchOption(
+ val radio by stringOption(
key = "radio",
default = null,
title = "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."
+ "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.",
)
- override val serial by stringPatchOption(
+ val serial by stringOption(
key = "serial",
default = null,
title = "Serial",
- description = "This field was deprecated in API level 26. Use getSerial() instead."
+ description = "This field was deprecated in API level 26. Use getSerial() instead.",
)
- override val sku by stringPatchOption(
+ val sku by stringOption(
key = "sku",
default = null,
title = "SKU",
- description = "The SKU of the hardware (from the kernel command line)."
+ description = "The SKU of the hardware (from the kernel command line).",
)
- override val socManufacturer by stringPatchOption(
+ val socManufacturer by stringOption(
key = "soc-manufacturer",
default = null,
title = "SOC Manufacturer",
- description = "The manufacturer of the device's primary system-on-chip."
+ description = "The manufacturer of the device's primary system-on-chip.",
)
- override val socModel by stringPatchOption(
+ val socModel by stringOption(
key = "soc-model",
default = null,
title = "SOC Model",
- description = "The model name of the device's primary system-on-chip."
+ description = "The model name of the device's primary system-on-chip.",
)
- override val tags by stringPatchOption(
+ val tags by stringOption(
key = "tags",
default = null,
title = "Tags",
- description = "Comma-separated tags describing the build, like \"unsigned,debug\"."
+ description = "Comma-separated tags describing the build, like \"unsigned,debug\".",
)
- override val time by longPatchOption(
+ val time by longOption(
key = "time",
default = null,
title = "Time",
- description = "The time at which the build was produced, given in milliseconds since the UNIX epoch."
+ description = "The time at which the build was produced, given in milliseconds since the UNIX epoch.",
)
- override val type by stringPatchOption(
+ val type by stringOption(
key = "type",
default = null,
title = "Type",
- description = "The type of build, like \"user\" or \"eng\"."
+ description = "The type of build, like \"user\" or \"eng\".",
)
- override val user by stringPatchOption(
+ val user by stringOption(
key = "user",
default = null,
title = "User",
- description = "The user."
+ description = "The user.",
)
-}
\ No newline at end of file
+
+ dependsOn(
+ baseSpoofBuildInfoPatch {
+ BuildInfo(
+ board,
+ bootloader,
+ brand,
+ cpuAbi,
+ cpuAbi2,
+ device,
+ display,
+ fingerprint,
+ hardware,
+ host,
+ id,
+ manufacturer,
+ model,
+ odmSku,
+ product,
+ radio,
+ serial,
+ sku,
+ socManufacturer,
+ socModel,
+ tags,
+ time,
+ type,
+ 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
new file mode 100644
index 000000000..b17eea94d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt
@@ -0,0 +1,50 @@
+@file:Suppress("unused")
+
+package app.revanced.patches.all.misc.connectivity.location.hide
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.all.misc.transformation.IMethodCall
+import app.revanced.patches.all.misc.transformation.fromMethodReference
+import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
+import app.revanced.util.getReference
+import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
+import com.android.tools.smali.dexlib2.iface.reference.MethodReference
+
+@Suppress("unused")
+val hideMockLocationPatch = bytecodePatch(
+ name = "Hide mock location",
+ description = "Prevents the app from knowing the device location is being mocked by a third party app.",
+ use = false,
+) {
+ dependsOn(
+ transformInstructionsPatch(
+ filterMap = filter@{ _, _, instruction, instructionIndex ->
+ val reference = instruction.getReference() ?: return@filter null
+ if (fromMethodReference(reference) == null) return@filter null
+
+ instruction to instructionIndex
+ },
+ transform = { method, entry ->
+ val (instruction, index) = entry
+ instruction as FiveRegisterInstruction
+
+ // Replace return value with a constant `false` boolean.
+ method.replaceInstruction(
+ index + 1,
+ "const/4 v${instruction.registerC}, 0x0",
+ )
+ },
+ ),
+ )
+}
+
+private enum class MethodCall(
+ override val definedClassName: String,
+ override val methodName: String,
+ override val methodParams: Array,
+ override val returnType: String,
+) : IMethodCall {
+ IsMock("Landroid/location/Location;", "isMock", emptyArray(), "Z"),
+ IsFromMockProvider("Landroid/location/Location;", "isFromMockProvider", emptyArray(), "Z"),
+}
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
new file mode 100644
index 000000000..b50ccfc88
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt
@@ -0,0 +1,105 @@
+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.patcher.patch.bytecodePatch
+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.*
+
+@Suppress("unused")
+val spoofSimCountryPatch = bytecodePatch(
+ name = "Spoof SIM country",
+ description = "Spoofs country information returned by the SIM card provider.",
+ use = false,
+) {
+ val countries = Locale.getISOCountries().associateBy { Locale("", it).displayCountry }
+
+ fun isoCountryPatchOption(
+ key: String,
+ title: String,
+ ) = stringOption(
+ key,
+ 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 },
+ )
+
+ val networkCountryIso by isoCountryPatchOption(
+ "networkCountryIso",
+ "Network ISO Country Code",
+ )
+
+ val simCountryIso by isoCountryPatchOption(
+ "simCountryIso",
+ "Sim ISO Country Code",
+ )
+
+ dependsOn(
+ transformInstructionsPatch(
+ filterMap = { _, _, instruction, instructionIndex ->
+ if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
+
+ val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null
+
+ val match = MethodCall.entries.firstOrNull { search ->
+ MethodUtil.methodSignaturesMatch(reference, search.reference)
+ } ?: return@transformInstructionsPatch null
+
+ val iso = when (match) {
+ MethodCall.NetworkCountryIso -> networkCountryIso
+ MethodCall.SimCountryIso -> simCountryIso
+ }?.lowercase()
+
+ iso?.let { instructionIndex to it }
+ },
+ transform = { mutableMethod, entry: Pair ->
+ transformMethodCall(entry, mutableMethod)
+ },
+ ),
+ )
+}
+
+private fun transformMethodCall(
+ entry: Pair,
+ mutableMethod: MutableMethod,
+) {
+ val (instructionIndex, methodCallValue) = entry
+
+ val register = mutableMethod.getInstruction(instructionIndex + 1).registerA
+
+ mutableMethod.replaceInstruction(
+ instructionIndex + 1,
+ "const-string v$register, \"$methodCallValue\"",
+ )
+}
+
+private enum class MethodCall(
+ val reference: MethodReference,
+) {
+ NetworkCountryIso(
+ ImmutableMethodReference(
+ "Landroid/telephony/TelephonyManager;",
+ "getNetworkCountryIso",
+ emptyList(),
+ "Ljava/lang/String;",
+ ),
+ ),
+ SimCountryIso(
+ ImmutableMethodReference(
+ "Landroid/telephony/TelephonyManager;",
+ "getSimCountryIso",
+ emptyList(),
+ "Ljava/lang/String;",
+ ),
+ ),
+}
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
new file mode 100644
index 000000000..3e086f95c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt
@@ -0,0 +1,224 @@
+package app.revanced.patches.all.misc.connectivity.wifi.spoof
+
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.all.misc.transformation.IMethodCall
+import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
+import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
+
+internal const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch"
+
+internal const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
+
+@Suppress("unused")
+val spoofWifiPatch = bytecodePatch(
+ name = "Spoof Wi-Fi connection",
+ description = "Spoofs an existing Wi-Fi connection.",
+ use = false,
+) {
+ extendWith("extensions/all/connectivity/wifi/spoof/spoof-wifi.rve")
+
+ dependsOn(
+ transformInstructionsPatch(
+ filterMap = { classDef, _, instruction, instructionIndex ->
+ filterMapInstruction35c(
+ EXTENSION_CLASS_DESCRIPTOR_PREFIX,
+ classDef,
+ instruction,
+ instructionIndex,
+ )
+ },
+ transform = { method, entry ->
+ val (methodType, instruction, instructionIndex) = entry
+ methodType.replaceInvokeVirtualWithExtension(
+ EXTENSION_CLASS_DESCRIPTOR,
+ method,
+ instruction,
+ instructionIndex,
+ )
+ },
+ ),
+ )
+}
+
+// Information about method calls we want to replace
+@Suppress("unused")
+private enum class MethodCall(
+ override val definedClassName: String,
+ override val methodName: String,
+ override val methodParams: Array,
+ override val returnType: String,
+) : IMethodCall {
+ GetSystemService1(
+ "Landroid/content/Context;",
+ "getSystemService",
+ arrayOf("Ljava/lang/String;"),
+ "Ljava/lang/Object;",
+ ),
+ GetSystemService2(
+ "Landroid/content/Context;",
+ "getSystemService",
+ arrayOf("Ljava/lang/Class;"),
+ "Ljava/lang/Object;",
+ ),
+ GetActiveNetworkInfo(
+ "Landroid/net/ConnectivityManager;",
+ "getActiveNetworkInfo",
+ arrayOf(),
+ "Landroid/net/NetworkInfo;",
+ ),
+ IsConnected(
+ "Landroid/net/NetworkInfo;",
+ "isConnected",
+ arrayOf(),
+ "Z",
+ ),
+ IsConnectedOrConnecting(
+ "Landroid/net/NetworkInfo;",
+ "isConnectedOrConnecting",
+ arrayOf(),
+ "Z",
+ ),
+ IsAvailable(
+ "Landroid/net/NetworkInfo;",
+ "isAvailable",
+ arrayOf(),
+ "Z",
+ ),
+ GetState(
+ "Landroid/net/NetworkInfo;",
+ "getState",
+ arrayOf(),
+ "Landroid/net/NetworkInfo\$State;",
+ ),
+ GetDetailedState(
+ "Landroid/net/NetworkInfo;",
+ "getDetailedState",
+ arrayOf(),
+ "Landroid/net/NetworkInfo\$DetailedState;",
+ ),
+ IsActiveNetworkMetered(
+ "Landroid/net/ConnectivityManager;",
+ "isActiveNetworkMetered",
+ arrayOf(),
+ "Z",
+ ),
+ GetActiveNetwork(
+ "Landroid/net/ConnectivityManager;",
+ "getActiveNetwork",
+ arrayOf(),
+ "Landroid/net/Network;",
+ ),
+ GetNetworkInfo(
+ "Landroid/net/ConnectivityManager;",
+ "getNetworkInfo",
+ arrayOf("Landroid/net/Network;"),
+ "Landroid/net/NetworkInfo;",
+ ),
+ HasTransport(
+ "Landroid/net/NetworkCapabilities;",
+ "hasTransport",
+ arrayOf("I"),
+ "Z",
+ ),
+ HasCapability(
+ "Landroid/net/NetworkCapabilities;",
+ "hasCapability",
+ arrayOf("I"),
+ "Z",
+ ),
+ RegisterBestMatchingNetworkCallback(
+ "Landroid/net/ConnectivityManager;",
+ "registerBestMatchingNetworkCallback",
+ arrayOf(
+ "Landroid/net/NetworkRequest;",
+ "Landroid/net/ConnectivityManager\$NetworkCallback;",
+ "Landroid/os/Handler;",
+ ),
+ "V",
+ ),
+ RegisterDefaultNetworkCallback1(
+ "Landroid/net/ConnectivityManager;",
+ "registerDefaultNetworkCallback",
+ arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
+ "V",
+ ),
+ RegisterDefaultNetworkCallback2(
+ "Landroid/net/ConnectivityManager;",
+ "registerDefaultNetworkCallback",
+ arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
+ "V",
+ ),
+ RegisterNetworkCallback1(
+ "Landroid/net/ConnectivityManager;",
+ "registerNetworkCallback",
+ arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
+ "V",
+ ),
+ RegisterNetworkCallback2(
+ "Landroid/net/ConnectivityManager;",
+ "registerNetworkCallback",
+ arrayOf("Landroid/net/NetworkRequest;", "Landroid/app/PendingIntent;"),
+ "V",
+ ),
+ RegisterNetworkCallback3(
+ "Landroid/net/ConnectivityManager;",
+ "registerNetworkCallback",
+ arrayOf(
+ "Landroid/net/NetworkRequest;",
+ "Landroid/net/ConnectivityManager\$NetworkCallback;",
+ "Landroid/os/Handler;",
+ ),
+ "V",
+ ),
+ RequestNetwork1(
+ "Landroid/net/ConnectivityManager;",
+ "requestNetwork",
+ arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
+ "V",
+ ),
+ RequestNetwork2(
+ "Landroid/net/ConnectivityManager;",
+ "requestNetwork",
+ arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"),
+ "V",
+ ),
+ RequestNetwork3(
+ "Landroid/net/ConnectivityManager;",
+ "requestNetwork",
+ arrayOf(
+ "Landroid/net/NetworkRequest;",
+ "Landroid/net/ConnectivityManager\$NetworkCallback;",
+ "Landroid/os/Handler;",
+ ),
+ "V",
+ ),
+ RequestNetwork4(
+ "Landroid/net/ConnectivityManager;",
+ "requestNetwork",
+ arrayOf("Landroid/net/NetworkRequest;", "Landroid/app/PendingIntent;"),
+ "V",
+ ),
+ RequestNetwork5(
+ "Landroid/net/ConnectivityManager;",
+ "requestNetwork",
+ arrayOf(
+ "Landroid/net/NetworkRequest;",
+ "Landroid/net/ConnectivityManager\$NetworkCallback;",
+ "Landroid/os/Handler;",
+ "I",
+ ),
+ "V",
+ ),
+ UnregisterNetworkCallback1(
+ "Landroid/net/ConnectivityManager;",
+ "unregisterNetworkCallback",
+ arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
+ "V",
+ ),
+ UnregisterNetworkCallback2(
+ "Landroid/net/ConnectivityManager;",
+ "unregisterNetworkCallback",
+ arrayOf("Landroid/app/PendingIntent;"),
+ "V",
+ ),
+}
diff --git a/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt
similarity index 58%
rename from src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt
index 4e3dedd32..97320c664 100644
--- a/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt
@@ -1,21 +1,16 @@
package app.revanced.patches.all.misc.debugging
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.resourcePatch
import org.w3c.dom.Element
-@Patch(
+@Suppress("unused")
+val enableAndroidDebuggingPatch = resourcePatch(
name = "Enable Android debugging",
description = "Enables Android debugging capabilities. This can slow down the app.",
use = false,
-)
-@Suppress("unused")
-object EnableAndroidDebuggingPatch : ResourcePatch() {
- override fun execute(context: ResourceContext) {
- context.xmlEditor["AndroidManifest.xml"].use { editor ->
- val document = editor.file
-
+) {
+ execute { context ->
+ context.document["AndroidManifest.xml"].use { document ->
val applicationNode =
document
.getElementsByTagName("application")
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
new file mode 100644
index 000000000..7256d1edb
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt
@@ -0,0 +1,58 @@
+package app.revanced.patches.all.misc.directory
+
+import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
+import app.revanced.patcher.extensions.InstructionExtensions.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.iface.instruction.formats.Instruction35c
+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
+
+@Suppress("unused")
+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(
+ transformInstructionsPatch(
+ filterMap = filter@{ _, _, instruction, instructionIndex ->
+ val reference = instruction.getReference() ?: return@filter null
+
+ if (!MethodUtil.methodSignaturesMatch(reference, MethodCall.GetDir.reference)) {
+ return@filter null
+ }
+
+ return@filter instructionIndex
+ },
+ transform = { method, index ->
+ val getDirInstruction = method.getInstruction(index)
+ val contextRegister = getDirInstruction.registerC
+ val dataRegister = getDirInstruction.registerD
+
+ method.replaceInstruction(
+ index,
+ "invoke-virtual { v$contextRegister, v$dataRegister }, " +
+ "Landroid/content/Context;->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;",
+ )
+ },
+ ),
+ )
+}
+
+private enum class MethodCall(
+ val reference: MethodReference,
+) {
+ GetDir(
+ ImmutableMethodReference(
+ "Landroid/content/Context;",
+ "getDir",
+ listOf("Ljava/lang/String;", "I"),
+ "Ljava/io/File;",
+ ),
+ ),
+}
diff --git a/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt
similarity index 54%
rename from src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt
index 7aaee12b4..a8bb80517 100644
--- a/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt
@@ -1,23 +1,22 @@
package app.revanced.patches.all.misc.hex
import app.revanced.patcher.patch.PatchException
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.registerNewPatchOption
-import app.revanced.patches.shared.misc.hex.BaseHexPatch
+import app.revanced.patcher.patch.rawResourcePatch
+import app.revanced.patcher.patch.stringsOption
+import app.revanced.patches.shared.misc.hex.Replacement
+import app.revanced.patches.shared.misc.hex.hexPatch
import app.revanced.util.Utils.trimIndentMultiline
-import app.revanced.patcher.patch.Patch as PatchClass
-@Patch(
+@Suppress("unused")
+val hexPatch = rawResourcePatch(
name = "Hex",
description = "Replaces a hexadecimal patterns of bytes of files in an APK.",
use = false,
-)
-@Suppress("unused")
-class HexPatch : BaseHexPatch() {
+) {
// TODO: Instead of stringArrayOption, use a custom option type to work around
// https://github.com/ReVanced/revanced-library/issues/48.
// Replace the custom option type with a stringArrayOption once the issue is resolved.
- private val replacementsOption by registerNewPatchOption, List>(
+ val replacements by stringsOption(
key = "replacements",
title = "Replacements",
description = """
@@ -34,22 +33,24 @@ class HexPatch : BaseHexPatch() {
'aa 01 02 FF|00 00 00 00|path/to/file'
""".trimIndentMultiline(),
required = true,
- valueType = "StringArray",
)
- override val replacements
- get() = replacementsOption!!.map { from ->
- val (pattern, replacementPattern, targetFilePath) = try {
- from.split("|", limit = 3)
- } catch (e: Exception) {
- throw PatchException(
- "Invalid input: $from.\n" +
- "Every pattern must be followed by a pipe ('|'), " +
- "the replacement pattern, another pipe ('|'), " +
- "and the path to the file to make the changes in relative to the APK root. ",
- )
- }
+ dependsOn(
+ hexPatch {
+ replacements!!.map { from ->
+ val (pattern, replacementPattern, targetFilePath) = try {
+ from.split("|", limit = 3)
+ } catch (e: Exception) {
+ throw PatchException(
+ "Invalid input: $from.\n" +
+ "Every pattern must be followed by a pipe ('|'), " +
+ "the replacement pattern, another pipe ('|'), " +
+ "and the path to the file to make the changes in relative to the APK root. ",
+ )
+ }
- Replacement(pattern, replacementPattern, targetFilePath)
- }
+ Replacement(pattern, replacementPattern, targetFilePath)
+ }.toSet()
+ },
+ )
}
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
new file mode 100644
index 000000000..abf7f002c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.all.misc.interaction.gestures
+
+import app.revanced.patcher.patch.resourcePatch
+
+@Suppress("unused")
+val predictiveBackGesturePatch = resourcePatch(
+ name = "Predictive back gesture",
+ description = "Enables the predictive back gesture introduced on Android 13.",
+ use = false,
+) {
+ execute { context ->
+ val flag = "android:enableOnBackInvokedCallback"
+
+ context.document["AndroidManifest.xml"].use { document ->
+ with(document.getElementsByTagName("application").item(0)) {
+ if (attributes.getNamedItem(flag) != null) return@with
+
+ document.createAttribute(flag)
+ .apply { value = "true" }
+ .let(attributes::setNamedItem)
+ }
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt
similarity index 81%
rename from src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt
index dc300df4a..26a3be2b0 100644
--- a/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt
@@ -1,28 +1,24 @@
package app.revanced.patches.all.misc.network
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patches.all.misc.debugging.EnableAndroidDebuggingPatch
+import app.revanced.patcher.patch.resourcePatch
+import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch
import app.revanced.util.Utils.trimIndentMultiline
import org.w3c.dom.Element
import java.io.File
-@Patch(
+@Suppress("unused")
+val overrideCertificatePinningPatch = resourcePatch(
name = "Override certificate pinning",
description = "Overrides certificate pinning, allowing to inspect traffic via a proxy.",
- dependencies = [EnableAndroidDebuggingPatch::class],
use = false,
-)
-@Suppress("unused")
-object OverrideCertificatePinningPatch : ResourcePatch() {
- override fun execute(context: ResourceContext) {
+) {
+ dependsOn(enableAndroidDebuggingPatch)
+
+ execute { context ->
val resXmlDirectory = context.get("res/xml")
// Add android:networkSecurityConfig="@xml/network_security_config" and the "networkSecurityConfig" attribute if it does not exist.
- context.xmlEditor["AndroidManifest.xml"].use { editor ->
- val document = editor.file
-
+ context.document["AndroidManifest.xml"].use { document ->
val applicationNode = document.getElementsByTagName("application").item(0) as Element
if (!applicationNode.hasAttribute("networkSecurityConfig")) {
@@ -58,4 +54,4 @@ object OverrideCertificatePinningPatch : ResourcePatch() {
)
}
}
-}
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..9565e8381
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt
@@ -0,0 +1,62 @@
+package app.revanced.patches.all.misc.packagename
+
+import app.revanced.patcher.patch.Option
+import app.revanced.patcher.patch.OptionException
+import app.revanced.patcher.patch.resourcePatch
+import app.revanced.patcher.patch.stringOption
+import org.w3c.dom.Element
+
+lateinit var packageNameOption: Option
+
+/**
+ * Set the package name to use.
+ * If this is called multiple times, the first call will set the package name.
+ *
+ * @param fallbackPackageName The package name to use if the user has not already specified a package name.
+ * @return The package name that was set.
+ * @throws OptionException.ValueValidationException If the package name is invalid.
+ */
+fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
+ val packageName = packageNameOption.value!!
+
+ return if (packageName == packageNameOption.default) {
+ fallbackPackageName.also { packageNameOption.value = it }
+ } else {
+ packageName
+ }
+}
+
+@Suppress("unused")
+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*)+\$"))
+ }
+
+ finalize { context ->
+ context.document["AndroidManifest.xml"].use { document ->
+
+ val replacementPackageName = packageNameOption.value
+
+ val manifest = document.getElementsByTagName("manifest").item(0) as Element
+ manifest.setAttribute(
+ "package",
+ if (replacementPackageName != packageNameOption.default) {
+ replacementPackageName
+ } else {
+ "${manifest.getAttribute("package")}.revanced"
+ },
+ )
+ }
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..9c13b7034
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt
@@ -0,0 +1,397 @@
+package app.revanced.patches.all.misc.resources
+
+import app.revanced.patcher.patch.Patch
+import app.revanced.patcher.patch.PatchException
+import app.revanced.patcher.patch.resourcePatch
+import app.revanced.patcher.util.Document
+import app.revanced.util.*
+import app.revanced.util.resource.ArrayResource
+import app.revanced.util.resource.BaseResource
+import app.revanced.util.resource.StringResource
+import org.w3c.dom.Node
+
+/**
+ * An identifier of an app. For example, `youtube`.
+ */
+private typealias AppId = String
+
+/**
+ * An identifier of a patch. For example, `ad.general.HideAdsPatch`.
+ */
+private typealias PatchId = String
+
+/**
+ * A set of resources of a patch.
+ */
+private typealias PatchResources = MutableSet
+
+/**
+ * A map of resources belonging to a patch.
+ */
+private typealias AppResources = MutableMap
+
+/**
+ * A map of resources belonging to an app.
+ */
+private typealias Resources = MutableMap
+
+/**
+ * The value of a resource.
+ * For example, `values` or `values-de`.
+ */
+private typealias Value = String
+
+/**
+ * A set of resources mapped by their value.
+ */
+private typealias MutableResources = MutableMap>
+
+/**
+ * A map of all resources associated by their value staged by [addResourcesPatch].
+ */
+private lateinit var stagedResources: Map
+
+/**
+ * A map of all resources added to the app by [addResourcesPatch].
+ */
+private val resources: MutableResources = mutableMapOf()
+
+/**
+ * Map of Crowdin locales to Android resource locale names.
+ *
+ * Fixme: Instead this patch should detect what locale regions are present in both patches and the target app,
+ * and automatically merge into the appropriate existing target file.
+ * So if a target app has only 'es', then the Crowdin file of 'es-rES' should merge into that.
+ * But if a target app has specific regions (such as 'pt-rBR'),
+ * then the Crowdin region specific file should merged into that.
+ */
+private val locales = mapOf(
+ "af-rZA" to "af",
+ "am-rET" to "am",
+ "ar-rSA" to "ar",
+ "as-rIN" to "as",
+ "az-rAZ" to "az",
+ "be-rBY" to "be",
+ "bg-rBG" to "bg",
+ "bn-rBD" to "bn",
+ "bs-rBA" to "bs",
+ "ca-rES" to "ca",
+ "cs-rCZ" to "cs",
+ "da-rDK" to "da",
+ "de-rDE" to "de",
+ "el-rGR" to "el",
+ "es-rES" to "es",
+ "et-rEE" to "et",
+ "eu-rES" to "eu",
+ "fa-rIR" to "fa",
+ "fi-rFI" to "fi",
+ "fil-rPH" to "tl",
+ "fr-rFR" to "fr",
+ "ga-rIE" to "ga",
+ "gl-rES" to "gl",
+ "gu-rIN" to "gu",
+ "hi-rIN" to "hi",
+ "hr-rHR" to "hr",
+ "hu-rHU" to "hu",
+ "hy-rAM" to "hy",
+ "in-rID" to "in",
+ "is-rIS" to "is",
+ "it-rIT" to "it",
+ "iw-rIL" to "iw",
+ "ja-rJP" to "ja",
+ "ka-rGE" to "ka",
+ "kk-rKZ" to "kk",
+ "km-rKH" to "km",
+ "kn-rIN" to "kn",
+ "ko-rKR" to "ko",
+ "ky-rKG" to "ky",
+ "lo-rLA" to "lo",
+ "lt-rLT" to "lt",
+ "lv-rLV" to "lv",
+ "mk-rMK" to "mk",
+ "ml-rIN" to "ml",
+ "mn-rMN" to "mn",
+ "mr-rIN" to "mr",
+ "ms-rMY" to "ms",
+ "my-rMM" to "my",
+ "nb-rNO" to "nb",
+ "ne-rIN" to "ne",
+ "nl-rNL" to "nl",
+ "or-rIN" to "or",
+ "pa-rIN" to "pa",
+ "pl-rPL" to "pl",
+ "pt-rBR" to "pt-rBR",
+ "pt-rPT" to "pt-rPT",
+ "ro-rRO" to "ro",
+ "ru-rRU" to "ru",
+ "si-rLK" to "si",
+ "sk-rSK" to "sk",
+ "sl-rSI" to "sl",
+ "sq-rAL" to "sq",
+ "sr-rCS" to "b+sr+Latn",
+ "sr-rSP" to "sr",
+ "sv-rSE" to "sv",
+ "sw-rKE" to "sw",
+ "ta-rIN" to "ta",
+ "te-rIN" to "te",
+ "th-rTH" to "th",
+ "tl-rPH" to "tl",
+ "tr-rTR" to "tr",
+ "uk-rUA" to "uk",
+ "ur-rIN" to "ur",
+ "uz-rUZ" to "uz",
+ "vi-rVN" to "vi",
+ "zh-rCN" to "zh-rCN",
+ "zh-rTW" to "zh-rTW",
+ "zu-rZA" to "zu",
+)
+
+/**
+ * Adds a [BaseResource] to the map using [MutableMap.getOrPut].
+ *
+ * @param value The value of the resource. For example, `values` or `values-de`.
+ * @param resource The resource to add.
+ *
+ * @return True if the resource was added, false if it already existed.
+ */
+fun addResource(
+ value: Value,
+ resource: BaseResource,
+) = resources.getOrPut(value, ::mutableSetOf).add(resource)
+
+/**
+ * Adds a list of [BaseResource]s to the map using [MutableMap.getOrPut].
+ *
+ * @param value The value of the resource. For example, `values` or `values-de`.
+ * @param resources The resources to add.
+ *
+ * @return True if the resources were added, false if they already existed.
+ */
+fun addResources(
+ value: Value,
+ resources: Iterable,
+) = app.revanced.patches.all.misc.resources.resources.getOrPut(value, ::mutableSetOf).addAll(resources)
+
+/**
+ * Adds a [StringResource].
+ *
+ * @param name The name of the string resource.
+ * @param value The value of the string resource.
+ * @param formatted Whether the string resource is formatted. Defaults to `true`.
+ * @param resourceValue The value of the resource. For example, `values` or `values-de`.
+ *
+ * @return True if the resource was added, false if it already existed.
+ */
+fun addResources(
+ name: String,
+ value: String,
+ formatted: Boolean = true,
+ resourceValue: Value = "values",
+) = addResource(resourceValue, StringResource(name, value, formatted))
+
+/**
+ * Adds an [ArrayResource].
+ *
+ * @param name The name of the array resource.
+ * @param items The items of the array resource.
+ *
+ * @return True if the resource was added, false if it already existed.
+ */
+fun addResources(
+ name: String,
+ items: List,
+) = addResource("values", ArrayResource(name, items))
+
+/**
+ * Puts all resources of any [Value] staged in [stagedResources] for the [Patch] to [addResources].
+ *
+ * @param patch The [Patch] of the patch to stage resources for.
+ * @param parseIds A function that parses a set of [PatchId] each mapped to an [AppId] from the given [Patch].
+ * This is used to access the resources in [addResources] to stage them in [stagedResources].
+ * The default implementation assumes that the [Patch] has a name and declares packages it is compatible with.
+ *
+ * @return True if any resources were added, false if none were added.
+ *
+ * @see addResourcesPatch
+ */
+fun addResources(
+ 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")
+
+ buildMap> {
+ packages.forEach { (appId, _) ->
+ getOrPut(appId) { mutableSetOf() }.add(patchId)
+ }
+ }
+ },
+): Boolean {
+ var result = false
+
+ // Stage resources for the given patch to addResourcesPatch associated with their value.
+ parseIds(patch).forEach { (appId, patchIds) ->
+ patchIds.forEach { patchId ->
+ stagedResources.forEach { (value, resources) ->
+ resources[appId]?.get(patchId)?.let { patchResources ->
+ if (addResources(value, patchResources)) result = true
+ }
+ }
+ }
+ }
+
+ return result
+}
+
+/**
+ * Puts all resources for the given [appId] and [patchId] staged in [addResources] to [addResourcesPatch].
+ *
+ *
+ * @return True if any resources were added, false if none were added.
+ *
+ * @see addResourcesPatch
+ */
+fun addResources(
+ appId: AppId,
+ patchId: String,
+) = stagedResources.forEach { (value, resources) ->
+ resources[appId]?.get(patchId)?.let { patchResources ->
+ addResources(value, patchResources)
+ }
+}
+
+val addResourcesPatch = resourcePatch(
+ description = "Add resources such as strings or arrays to the app.",
+) {
+ /*
+ The strategy of this patch is to stage resources present in `/resources/addresources`.
+ These resources are organized by their respective value and patch.
+
+ On addResourcesPatch#execute, all resources are staged in a temporary map.
+ After that, other patches that depend on addResourcesPatch can call
+ addResourcesPatch#invoke(Patch) to stage resources belonging to that patch
+ 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.
+ */
+ execute { context ->
+ stagedResources = buildMap {
+ /**
+ * Puts resources under `/resources/addresources//.xml` into the map.
+ *
+ * @param sourceValue The source value of the resource. For example, `values` or `values-de-rDE`.
+ * @param destValue The destination value of the resource. For example, 'values' or 'values-de'.
+ * @param resourceKind The kind of the resource. For example, `strings` or `arrays`.
+ * @param transform A function that transforms the [Node]s from the XML files to a [BaseResource].
+ */
+ fun addResources(
+ sourceValue: Value,
+ destValue: Value = sourceValue,
+ resourceKind: String,
+ transform: (Node) -> BaseResource,
+ ) {
+ inputStreamFromBundledResource(
+ "addresources",
+ "$sourceValue/$resourceKind.xml",
+ )?.let { stream ->
+ // Add the resources associated with the given value to the map,
+ // instead of overwriting it.
+ // This covers the example case such as adding strings and arrays of the same value.
+ getOrPut(destValue, ::mutableMapOf).apply {
+ context.document[stream].use { document ->
+ document.getElementsByTagName("app").asSequence().forEach { app ->
+ val appId = app.attributes.getNamedItem("id").textContent
+
+ getOrPut(appId, ::mutableMapOf).apply {
+ app.forEachChildElement { patch ->
+ val patchId = patch.attributes.getNamedItem("id").textContent
+
+ getOrPut(patchId, ::mutableSetOf).apply {
+ patch.forEachChildElement { resourceNode ->
+ val resource = transform(resourceNode)
+
+ add(resource)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Stage all resources to a temporary map.
+ // Staged resources consumed by addResourcesPatch#invoke(Patch)
+ // are later used in addResourcesPatch#finalize.
+ try {
+ val addStringResources = { source: Value, dest: Value ->
+ addResources(source, dest, "strings", StringResource::fromNode)
+ }
+ locales.forEach { (source, dest) -> addStringResources("values-$source", "values-$dest") }
+ addStringResources("values", "values")
+
+ addResources("values", "values", "arrays", ArrayResource::fromNode)
+ } catch (e: Exception) {
+ throw PatchException("Failed to read resources", e)
+ }
+ }
+ }
+
+ /**
+ * Adds all resources staged in [addResourcesPatch] to the app.
+ * This is called after all patches that depend on [addResourcesPatch] have been executed.
+ */
+ finalize { context ->
+ operator fun MutableMap>.invoke(
+ value: Value,
+ resource: BaseResource,
+ ) {
+ // TODO: Fix open-closed principle violation by modifying BaseResource#serialize so that it accepts
+ // a Value and the map of documents. It will then get or put the document suitable for its resource type
+ // to serialize itself to it.
+ val resourceFileName =
+ when (resource) {
+ is StringResource -> "strings"
+ is ArrayResource -> "arrays"
+ else -> throw NotImplementedError("Unsupported resource type")
+ }
+
+ getOrPut(resourceFileName) {
+ val targetFile =
+ context["res/$value/$resourceFileName.xml"].also {
+ it.parentFile?.mkdirs()
+
+ if (it.createNewFile()) {
+ it.writeText("\n\n")
+ }
+ }
+
+ context.document[targetFile.path].let { document ->
+
+ // Save the target node here as well
+ // in order to avoid having to call document.getNode("resources")
+ // but also save the document so that it can be closed later.
+ document to document.getNode("resources")
+ }
+ }.let { (_, targetNode) ->
+ targetNode.addResource(resource) { invoke(value, it) }
+ }
+ }
+
+ resources.forEach { (value, resources) ->
+ // A map of document associated by their kind (e.g. strings, arrays).
+ // Each document is accompanied by the target node to which resources are added.
+ // A map is used because Map#getOrPut allows opening a new document for the duration of a resource value.
+ // This is done to prevent having to open the files for every resource that is added.
+ // Instead, it is cached once and reused for resources of the same value.
+ // This map is later accessed to close all documents for the current resource value.
+ val documents = mutableMapOf>()
+
+ resources.forEach { resource -> documents(value, resource) }
+
+ documents.values.forEach { (document, _) -> document.close() }
+ }
+ }
+}
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
new file mode 100644
index 000000000..3633541e7
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt
@@ -0,0 +1,83 @@
+package app.revanced.patches.all.misc.screencapture
+
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patcher.patch.resourcePatch
+import app.revanced.patches.all.misc.transformation.IMethodCall
+import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
+import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
+import org.w3c.dom.Element
+
+private val removeCaptureRestrictionResourcePatch = resourcePatch(
+ description = "Sets allowAudioPlaybackCapture in manifest to true.",
+) {
+ execute { context ->
+ context.document["AndroidManifest.xml"].use { document ->
+ // Get the application node.
+ val applicationNode =
+ document
+ .getElementsByTagName("application")
+ .item(0) as Element
+
+ // Set allowAudioPlaybackCapture attribute to true.
+ applicationNode.setAttribute("android:allowAudioPlaybackCapture", "true")
+ }
+ }
+}
+
+private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
+ "Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch"
+private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
+
+@Suppress("unused")
+val removeScreenCaptureRestrictionPatch = bytecodePatch(
+ name = "Remove screen capture restriction",
+ description = "Removes the restriction of capturing audio from apps that normally wouldn't allow it.",
+ use = false,
+) {
+ extendWith("extensions/all/screencapture/remove-screen-capture-restriction.rve")
+
+ dependsOn(
+ removeCaptureRestrictionResourcePatch,
+ transformInstructionsPatch(
+ filterMap = { classDef, _, instruction, instructionIndex ->
+ filterMapInstruction35c(
+ EXTENSION_CLASS_DESCRIPTOR_PREFIX,
+ classDef,
+ instruction,
+ instructionIndex,
+ )
+ },
+ transform = { mutableMethod, entry ->
+ val (methodType, instruction, instructionIndex) = entry
+ methodType.replaceInvokeVirtualWithExtension(
+ EXTENSION_CLASS_DESCRIPTOR,
+ mutableMethod,
+ instruction,
+ instructionIndex,
+ )
+ },
+ ),
+ )
+}
+
+// Information about method calls we want to replace
+@Suppress("unused")
+private enum class MethodCall(
+ override val definedClassName: String,
+ override val methodName: String,
+ override val methodParams: Array,
+ override val returnType: String,
+) : IMethodCall {
+ SetAllowedCapturePolicySingle(
+ "Landroid/media/AudioAttributes\$Builder;",
+ "setAllowedCapturePolicy",
+ arrayOf("I"),
+ "Landroid/media/AudioAttributes\$Builder;",
+ ),
+ SetAllowedCapturePolicyGlobal(
+ "Landroid/media/AudioManager;",
+ "setAllowedCapturePolicy",
+ arrayOf("I"),
+ "V",
+ ),
+}
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
new file mode 100644
index 000000000..af689f5ed
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt
@@ -0,0 +1,97 @@
+package app.revanced.patches.all.misc.screenshot
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.all.misc.transformation.IMethodCall
+import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
+import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
+import com.android.tools.smali.dexlib2.iface.reference.FieldReference
+
+private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
+ "Lapp/revanced/extension/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
+private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
+
+@Suppress("unused")
+val removeScreenshotRestrictionPatch = bytecodePatch(
+ name = "Remove screenshot restriction",
+ description = "Removes the restriction of taking screenshots in apps that normally wouldn't allow it.",
+ use = false,
+) {
+ extendWith("extensions/all/screenshot/remove-screenshot-restriction.rve")
+
+ dependsOn(
+ // Remove the restriction of taking screenshots.
+ transformInstructionsPatch(
+ filterMap = { classDef, _, instruction, instructionIndex ->
+ filterMapInstruction35c(
+ EXTENSION_CLASS_DESCRIPTOR_PREFIX,
+ classDef,
+ instruction,
+ instructionIndex,
+ )
+ },
+ transform = { mutableMethod, entry ->
+ val (methodType, instruction, instructionIndex) = entry
+ methodType.replaceInvokeVirtualWithExtension(
+ EXTENSION_CLASS_DESCRIPTOR,
+ mutableMethod,
+ instruction,
+ instructionIndex,
+ )
+ },
+ ),
+ // Modify layout params.
+ transformInstructionsPatch(
+ filterMap = { _, _, instruction, instructionIndex ->
+ if (instruction.opcode != Opcode.IPUT) {
+ return@transformInstructionsPatch null
+ }
+
+ val instruction22c = instruction as Instruction22c
+ val fieldReference = instruction22c.reference as FieldReference
+
+ if (fieldReference.definingClass != "Landroid/view/WindowManager\$LayoutParams;" ||
+ fieldReference.name != "flags" ||
+ fieldReference.type != "I"
+ ) {
+ return@transformInstructionsPatch null
+ }
+
+ Pair(instruction22c, instructionIndex)
+ },
+ transform = { mutableMethod, entry ->
+ val (instruction, index) = entry
+ val register = instruction.registerA
+
+ mutableMethod.addInstructions(
+ index,
+ "and-int/lit16 v$register, v$register, -0x2001",
+ )
+ },
+ ),
+ )
+}
+
+// Information about method calls we want to replace
+@Suppress("unused")
+private enum class MethodCall(
+ override val definedClassName: String,
+ override val methodName: String,
+ override val methodParams: Array,
+ override val returnType: String,
+) : IMethodCall {
+ AddFlags(
+ "Landroid/view/Window;",
+ "addFlags",
+ arrayOf("I"),
+ "V",
+ ),
+ SetFlags(
+ "Landroid/view/Window;",
+ "setFlags",
+ arrayOf("I", "I"),
+ "V",
+ ),
+}
diff --git a/src/main/kotlin/app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt
similarity index 63%
rename from src/main/kotlin/app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt
index 6e7fbb936..c2e39dc24 100644
--- a/src/main/kotlin/app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt
@@ -1,26 +1,23 @@
-package app.revanced.patches.all.shortcut.sharetargets
+package app.revanced.patches.all.misc.shortcut.sharetargets
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.asSequence
import app.revanced.util.getNode
import org.w3c.dom.Element
import java.io.FileNotFoundException
import java.util.logging.Logger
-@Patch(
+@Suppress("unused")
+val removeShareTargetsPatch = resourcePatch(
name = "Remove share targets",
description = "Removes share targets like directly sharing to a frequent contact.",
use = false,
-)
-@Suppress("unused")
-object RemoveShareTargetsPatch : ResourcePatch() {
- override fun execute(context: ResourceContext) {
+) {
+ execute { context ->
try {
context.document["res/xml/shortcuts.xml"]
} catch (_: FileNotFoundException) {
- return Logger.getLogger(this::class.java.name).warning("The app has no shortcuts")
+ return@execute Logger.getLogger(this::class.java.name).warning("The app has no shortcuts")
}.use { document ->
val rootNode = document.getNode("shortcuts") as? Element ?: return@use
diff --git a/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt
similarity index 67%
rename from src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt
index 5736ff247..0c39436d1 100644
--- a/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt
@@ -18,8 +18,8 @@ interface IMethodCall {
/**
* Replaces an invoke-virtual instruction with an invoke-static instruction,
- * which calls a static replacement method in the respective integrations class.
- * The method definition in the integrations class is expected to be the same,
+ * which calls a static replacement method in the respective extension class.
+ * The method definition in the extension class is expected to be the same,
* except that the method should be static and take as a first parameter
* an instance of the class, in which the original method was defined in.
*
@@ -27,56 +27,58 @@ interface IMethodCall {
*
* original method: Window#setFlags(int, int)
*
- * replacement method: Integrations#setFlags(Window, int, int)
+ * replacement method: Extension#setFlags(Window, int, int)
*/
- fun replaceInvokeVirtualWithIntegrations(
+ fun replaceInvokeVirtualWithExtension(
definingClassDescriptor: String,
method: MutableMethod,
instruction: Instruction35c,
- instructionIndex: Int
+ instructionIndex: Int,
) {
val registers = arrayOf(
instruction.registerC,
instruction.registerD,
instruction.registerE,
instruction.registerF,
- instruction.registerG
+ instruction.registerG,
)
val argsNum = methodParams.size + 1 // + 1 for instance of definedClassName
if (argsNum > registers.size) {
// should never happen, but just to be sure (also for the future) a safety check
throw RuntimeException(
- "Not enough registers for ${definedClassName}#${methodName}: " +
- "Required $argsNum registers, but only got ${registers.size}."
+ "Not enough registers for $definedClassName#$methodName: " +
+ "Required $argsNum registers, but only got ${registers.size}.",
)
}
- val args = registers.take(argsNum).joinToString(separator = ", ") { reg -> "v${reg}" }
- val replacementMethodDefinition =
- "${methodName}(${definedClassName}${methodParams.joinToString(separator = "")})${returnType}"
+ val args = registers.take(argsNum).joinToString(separator = ", ") { reg -> "v$reg" }
+ val replacementMethod =
+ "$methodName(${definedClassName}${methodParams.joinToString(separator = "")})$returnType"
method.replaceInstruction(
instructionIndex,
- "invoke-static { $args }, ${definingClassDescriptor}->${replacementMethodDefinition}"
+ "invoke-static { $args }, $definingClassDescriptor->$replacementMethod",
)
}
}
-inline fun fromMethodReference(methodReference: MethodReference)
+inline fun fromMethodReference(
+ methodReference: MethodReference,
+)
where E : Enum, E : IMethodCall = enumValues().firstOrNull { search ->
- search.definedClassName == methodReference.definingClass
- && search.methodName == methodReference.name
- && methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams)
- && search.returnType == methodReference.returnType
+ search.definedClassName == methodReference.definingClass &&
+ search.methodName == methodReference.name &&
+ methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams) &&
+ search.returnType == methodReference.returnType
}
inline fun filterMapInstruction35c(
- integrationsClassDescriptorPrefix: String,
+ extensionClassDescriptorPrefix: String,
classDef: ClassDef,
instruction: Instruction,
- instructionIndex: Int
+ instructionIndex: Int,
): Instruction35cInfo? where E : Enum, E : IMethodCall {
- if (classDef.type.startsWith(integrationsClassDescriptorPrefix)) {
+ if (classDef.startsWith(extensionClassDescriptorPrefix)) {
// avoid infinite recursion
return null
}
diff --git a/src/main/kotlin/app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt
similarity index 79%
rename from src/main/kotlin/app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt
index e651c4d63..48d9ffa58 100644
--- a/src/main/kotlin/app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt
@@ -1,24 +1,16 @@
package app.revanced.patches.all.misc.transformation
-import app.revanced.patcher.data.BytecodeContext
-import app.revanced.patcher.patch.BytecodePatch
+import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.util.findMutableMethodOf
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
-@Suppress("MemberVisibilityCanBePrivate")
-abstract class BaseTransformInstructionsPatch : BytecodePatch(emptySet()) {
- abstract fun filterMap(
- classDef: ClassDef,
- method: Method,
- instruction: Instruction,
- instructionIndex: Int,
- ): T?
-
- abstract fun transform(mutableMethod: MutableMethod, entry: T)
-
+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? {
return method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) ->
@@ -26,7 +18,7 @@ abstract class BaseTransformInstructionsPatch : BytecodePatch(emptySet()) {
}
}
- override fun execute(context: BytecodeContext) {
+ execute { context ->
// Find all methods to patch
buildMap {
context.classes.forEach { classDef ->
diff --git a/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt
similarity index 69%
rename from src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt
index 5170332db..b8bfb4432 100644
--- a/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt
@@ -1,22 +1,19 @@
package app.revanced.patches.all.misc.versioncode
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.intPatchOption
+import app.revanced.patcher.patch.intOption
+import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.getNode
import org.w3c.dom.Element
-@Patch(
+@Suppress("unused")
+val changeVersionCodePatch = resourcePatch(
name = "Change version code",
description = "Changes the version code of the app. By default the highest version code is set. " +
"This allows older versions of an app to be installed " +
"if their version code is set to the same or a higher value and can stop app stores to update the app.",
use = false,
-)
-@Suppress("unused")
-object ChangeVersionCodePatch : ResourcePatch() {
- private val versionCode by intPatchOption(
+) {
+ val versionCode by intOption(
key = "versionCode",
default = Int.MAX_VALUE,
values = mapOf(
@@ -26,11 +23,9 @@ object ChangeVersionCodePatch : ResourcePatch() {
title = "Version code",
description = "The version code to use",
required = true,
- ) {
- it!! >= 1
- }
+ ) { versionCode -> versionCode!! >= 1 }
- override fun execute(context: ResourceContext) {
+ execute { context ->
context.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
new file mode 100644
index 000000000..5a726b791
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.amazon
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val deepLinkingPatch = 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")
+
+ val deepLinkingMatch by deepLinkingFingerprint()
+
+ execute {
+ deepLinkingMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt
new file mode 100644
index 000000000..1339149f4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.amazon
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val deepLinkingFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE)
+ returns("Z")
+ parameters("L")
+ strings("https://www.", "android.intent.action.VIEW")
+}
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
new file mode 100644
index 000000000..bbe71e8c7
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.backdrops.misc.pro
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.Opcode
+
+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
new file mode 100644
index 000000000..144f165de
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt
@@ -0,0 +1,27 @@
+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")
+val proUnlockPatch = bytecodePatch(
+ name = "Pro unlock",
+) {
+ compatibleWith("com.backdrops.wallpapers")
+
+ val proUnlockMatch by proUnlockFingerprint()
+
+ execute {
+ val registerIndex = proUnlockMatch.patternMatch!!.endIndex - 1
+
+ proUnlockMatch.mutableMethod.apply {
+ val register = getInstruction(registerIndex).registerA
+ addInstruction(
+ proUnlockMatch.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
new file mode 100644
index 000000000..96bbab5cc
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt
@@ -0,0 +1,7 @@
+package app.revanced.patches.bandcamp.limitations
+
+import app.revanced.patcher.fingerprint
+
+internal val handlePlaybackLimitsFingerprint = fingerprint {
+ strings("play limits processing track", "found 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
new file mode 100644
index 000000000..88302ef4c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.bandcamp.limitations
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val removePlayLimitsPatch = bytecodePatch(
+ name = "Remove play limits",
+ description = "Disables purchase nagging and playback limits of not purchased tracks.",
+) {
+ compatibleWith("com.bandcamp.android")
+
+ val handlePlaybackLimitsMatch by handlePlaybackLimitsFingerprint()
+
+ execute {
+ handlePlaybackLimitsMatch.mutableMethod.addInstructions(0, "return-void")
+ }
+}
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
new file mode 100644
index 000000000..a0a79f6db
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.cieid.restrictions.root
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val bypassRootChecksPatch = bytecodePatch(
+ name = "Bypass root checks",
+ description = "Removes the restriction to use the app with root permissions or on a custom ROM.",
+) {
+ compatibleWith("it.ipzs.cieid")
+
+ val checkRootMatch by checkRootFingerprint()
+
+ execute {
+ checkRootMatch.mutableMethod.addInstruction(1, "return-void")
+ }
+}
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
new file mode 100644
index 000000000..387b0a0be
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.cieid.restrictions.root
+
+import app.revanced.patcher.fingerprint
+
+internal val checkRootFingerprint = fingerprint {
+ custom { method, _ ->
+ method.name == "onResume" && method.definingClass == "Lit/ipzs/cieid/BaseActivity;"
+ }
+}
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
new file mode 100644
index 000000000..fa9eaf009
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt
@@ -0,0 +1,34 @@
+package app.revanced.patches.duolingo.ad
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
+import app.revanced.patcher.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
+
+@Suppress("unused")
+val disableAdsPatch = bytecodePatch(
+ "Disable ads",
+) {
+ compatibleWith("com.duolingo")
+
+ val initializeMonetizationDebugSettingsMatch by initializeMonetizationDebugSettingsFingerprint()
+
+ execute {
+ // Couple approaches to remove ads exist:
+ //
+ // MonetizationDebugSettings has a boolean value for "disableAds".
+ // OnboardingState has a getter to check if the user has any "adFreeSessions".
+ // 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.
+ initializeMonetizationDebugSettingsMatch.mutableMethod.apply {
+ val insertIndex = initializeMonetizationDebugSettingsMatch.patternMatch!!.startIndex
+ val register = getInstruction(insertIndex).registerA
+
+ addInstructions(
+ insertIndex,
+ "const/4 v$register, 0x1",
+ )
+ }
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt
new file mode 100644
index 000000000..59b0644d9
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.duolingo.ad
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val initializeMonetizationDebugSettingsFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ parameters(
+ "Z", // disableAds
+ "Z", // useDebugBilling
+ "Z", // showManageSubscriptions
+ "Z", // alwaysShowSuperAds
+ "Lcom/duolingo/debug/FamilyQuestOverride;",
+ )
+ opcodes(Opcode.IPUT_BOOLEAN)
+}
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
new file mode 100644
index 000000000..609b03cf1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt
@@ -0,0 +1,28 @@
+package app.revanced.patches.duolingo.debug
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
+import app.revanced.patcher.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
+
+@Suppress("unused")
+val enableDebugMenuPatch = bytecodePatch(
+ name = "Enable debug menu",
+ use = false,
+) {
+ compatibleWith("com.duolingo"("5.158.4"))
+
+ val initializeBuildConfigProviderMatch by initializeBuildConfigProviderFingerprint()
+
+ execute {
+ initializeBuildConfigProviderMatch.mutableMethod.apply {
+ val insertIndex = initializeBuildConfigProviderMatch.patternMatch!!.startIndex
+ val register = getInstruction(insertIndex).registerA
+
+ addInstructions(
+ insertIndex,
+ "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
new file mode 100644
index 000000000..543e40b43
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt
@@ -0,0 +1,19 @@
+package app.revanced.patches.duolingo.debug
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+/**
+ * The `BuildConfigProvider` class has two booleans:
+ *
+ * - `isChina`: (usually) compares "play" with "china"...except for builds in China
+ * - `isDebug`: compares "release" with "debug" <-- we want to force this to `true`
+ */
+
+internal val initializeBuildConfigProviderFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ opcodes(Opcode.IPUT_BOOLEAN)
+ strings("debug", "release", "china")
+}
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
new file mode 100644
index 000000000..7c732378f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt
@@ -0,0 +1,47 @@
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val baseModelMapperFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Lcom/facebook/graphql/modelutil/BaseModelWithTree;")
+ parameters("Ljava/lang/Class", "I", "I")
+ opcodes(
+ Opcode.SGET_OBJECT,
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.CONST_4,
+ Opcode.IF_EQ,
+ )
+}
+
+internal val getSponsoredDataModelTemplateFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("L")
+ parameters()
+ opcodes(
+ Opcode.CONST,
+ Opcode.CONST,
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.RETURN_OBJECT,
+ )
+ custom { _, classDef ->
+ classDef.type == "Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;"
+ }
+}
+
+internal val getStoryVisibilityFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Ljava/lang/String;")
+ opcodes(
+ Opcode.INSTANCE_OF,
+ Opcode.IF_NEZ,
+ Opcode.INSTANCE_OF,
+ Opcode.IF_NEZ,
+ Opcode.INSTANCE_OF,
+ 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
new file mode 100644
index 000000000..0c201f9fc
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt
@@ -0,0 +1,90 @@
+package app.revanced.patches.facebook.ads.mainfeed
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
+import baseModelMapperFingerprint
+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
+import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
+import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
+import getSponsoredDataModelTemplateFingerprint
+import getStoryVisibilityFingerprint
+
+@Suppress("unused")
+val hideSponsoredStoriesPatch = bytecodePatch(
+ name = "Hide 'Sponsored Stories'",
+) {
+ compatibleWith("com.facebook.katana")
+
+ val getStoryVisibilityMatch by getStoryVisibilityFingerprint()
+ val getSponsoredDataModelTemplateMatch by getSponsoredDataModelTemplateFingerprint()
+ val baseModelMapperMatch by baseModelMapperFingerprint()
+
+ execute {
+ val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateMatch.method
+ val baseModelMapperMethod = baseModelMapperMatch.method
+ val baseModelWithTreeType = baseModelMapperMethod.returnType
+
+ val graphQlStoryClassDescriptor = "Lcom/facebook/graphql/model/GraphQLStory;"
+
+ // The "SponsoredDataModelTemplate" methods has the ids in its body to extract sponsored data
+ // from GraphQL models, but targets the wrong derived type of "BaseModelWithTree". Since those ids
+ // could change in future version, we need to extract them and call the base implementation directly.
+ val getSponsoredDataHelperMethod = ImmutableMethod(
+ getStoryVisibilityMatch.classDef.type,
+ "getSponsoredData",
+ listOf(ImmutableMethodParameter(graphQlStoryClassDescriptor, null, null)),
+ baseModelWithTreeType,
+ AccessFlags.PRIVATE.value or AccessFlags.STATIC.value,
+ null,
+ null,
+ MutableMethodImplementation(4),
+ ).toMutable().apply {
+ // Extract the ids of the original method. These ids seem to correspond to model types for
+ // GraphQL data structure. They are then fed to a method of BaseModelWithTree that populate
+ // and cast the requested GraphQL subtype. The Ids are found in the two first "CONST" instructions.
+ val constInstructions = sponsoredDataModelTemplateMethod.implementation!!.instructions
+ .asSequence()
+ .filterIsInstance()
+ .take(2)
+ .toList()
+
+ val storyTypeId = constInstructions[0].narrowLiteral
+ val sponsoredDataTypeId = constInstructions[1].narrowLiteral
+
+ addInstructions(
+ """
+ const-class v2, $baseModelWithTreeType
+ const v1, $storyTypeId
+ const v0, $sponsoredDataTypeId
+ invoke-virtual {p0, v2, v1, v0}, $baseModelMapperMethod
+ move-result-object v0
+ check-cast v0, $baseModelWithTreeType
+ return-object v0
+ """,
+ )
+ }
+
+ getStoryVisibilityMatch.mutableClass.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.
+ getStoryVisibilityMatch.mutableMethod.addInstructionsWithLabels(
+ getStoryVisibilityMatch.patternMatch!!.startIndex,
+ """
+ instance-of v0, p0, $graphQlStoryClassDescriptor
+ if-eqz v0, :resume_normal
+ invoke-static {p0}, $getSponsoredDataHelperMethod
+ move-result-object v0
+ if-eqz v0, :resume_normal
+ const-string v0, "GONE"
+ return-object v0
+ :resume_normal
+ nop
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..293d7ee82
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.facebook.ads.story
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
+
+internal val adsInsertionFingerprint = fieldFingerprint(
+ fieldValue = "AdBucketDataSourceUtil\$attemptAdsInsertion\$1",
+)
+
+internal val fetchMoreAdsFingerprint = fieldFingerprint(
+ 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
+ }
+ }
+}
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
new file mode 100644
index 000000000..7699b7be1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.facebook.ads.story
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideStoryAdsPatch = bytecodePatch(
+ name = "Hide story ads",
+ description = "Hides the ads in the Facebook app stories.",
+) {
+ compatibleWith("com.facebook.katana")
+
+ val fetchMoreAdsMatch by fetchMoreAdsFingerprint()
+ val adsInsertionMatch by adsInsertionFingerprint()
+
+ execute {
+ setOf(fetchMoreAdsMatch, adsInsertionMatch).forEach { match ->
+ match.mutableMethod.replaceInstruction(0, "return-void")
+ }
+ }
+}
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
new file mode 100644
index 000000000..4bdeaf4a5
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt
@@ -0,0 +1,27 @@
+package app.revanced.patches.finanzonline.detection.bootloader
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val bootloaderDetectionPatch = bytecodePatch(
+ name = "Remove bootloader detection",
+ description = "Removes the check for an unlocked bootloader.",
+) {
+ compatibleWith("at.gv.bmf.bmf2go")
+
+ val createKeyMatch by createKeyFingerprint()
+ val bootStateMatch by bootStateFingerprint()
+
+ execute {
+ setOf(createKeyMatch, bootStateMatch).forEach { match ->
+ match.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/BootStateFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt
similarity index 57%
rename from src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/BootStateFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt
index f76ab78aa..4c8ee1c54 100644
--- a/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/BootStateFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt
@@ -1,14 +1,14 @@
-package app.revanced.patches.finanzonline.detection.bootloader.fingerprints
+package app.revanced.patches.finanzonline.detection.bootloader
-import app.revanced.patcher.fingerprint.MethodFingerprint
-import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#isBootStateOk (3.0.1)
-internal object BootStateFingerprint : MethodFingerprint(
- "Z",
- accessFlags = AccessFlags.PUBLIC.value,
- opcodes = listOf(
+internal val bootStateFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("Z")
+ opcodes(
Opcode.INVOKE_DIRECT,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
@@ -27,4 +27,11 @@ internal object BootStateFingerprint : MethodFingerprint(
Opcode.MOVE,
Opcode.RETURN
)
-)
+}
+
+// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#createKey (3.0.1)
+internal val createKeyFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("Z")
+ strings("attestation", "SHA-256", "random", "EC", "AndroidKeyStore")
+}
\ No newline at end of file
diff --git a/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/fingerprints/RootDetectionFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt
similarity index 53%
rename from src/main/kotlin/app/revanced/patches/finanzonline/detection/root/fingerprints/RootDetectionFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt
index 50c7f76a8..fec7f1249 100644
--- a/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/fingerprints/RootDetectionFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt
@@ -1,16 +1,15 @@
-package app.revanced.patches.finanzonline.detection.root.fingerprints
+package app.revanced.patches.finanzonline.detection.root
-import app.revanced.patcher.extensions.or
-import app.revanced.patcher.fingerprint.MethodFingerprint
-import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.RootDetection#isRooted (3.0.1)
-internal object RootDetectionFingerprint : MethodFingerprint(
- "L",
- accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
- parameters = listOf("L"),
- opcodes = listOf(
+internal val rootDetectionFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("L")
+ parameters("L")
+ opcodes(
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.INVOKE_VIRTUAL,
@@ -19,4 +18,4 @@ internal object RootDetectionFingerprint : MethodFingerprint(
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT
)
-)
+}
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
new file mode 100644
index 000000000..9144bf749
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.finanzonline.detection.root
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val rootDetectionPatch = bytecodePatch(
+ name = "Remove root detection",
+ description = "Removes the check for root permissions.",
+) {
+ compatibleWith("at.gv.bmf.bmf2go")
+
+ val rootDetectionMatch by rootDetectionFingerprint()
+
+ execute {
+ rootDetectionMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ sget-object v0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;
+ return-object v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..81cbb8c58
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt
@@ -0,0 +1,25 @@
+package app.revanced.patches.googlenews.customtabs
+
+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 enableCustomTabsPatch = bytecodePatch(
+ name = "Enable CustomTabs",
+ description = "Enables CustomTabs to open articles in your default browser.",
+) {
+ compatibleWith("com.google.android.apps.magazines")
+
+ val launchCustomTabMatch by launchCustomTabFingerprint()
+
+ execute {
+ launchCustomTabMatch.mutableMethod.apply {
+ val checkIndex = launchCustomTabMatch.patternMatch!!.endIndex + 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
new file mode 100644
index 000000000..8880c010e
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.googlenews.customtabs
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val launchCustomTabFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ opcodes(
+ Opcode.IPUT_OBJECT,
+ Opcode.CONST_4,
+ Opcode.IPUT,
+ Opcode.CONST_4,
+ Opcode.IPUT_BOOLEAN,
+ )
+ custom { _, classDef -> classDef.endsWith("CustomTabsArticleLauncher;") }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/ExtensionPatch.kt
new file mode 100644
index 000000000..281872662
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/ExtensionPatch.kt
@@ -0,0 +1,6 @@
+package app.revanced.patches.googlenews.misc.extension
+
+import app.revanced.patches.googlenews.misc.extension.hooks.startActivityInitHook
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val extensionPatch = sharedExtensionPatch(startActivityInitHook)
diff --git a/src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/fingerprints/StartActivityInitFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt
similarity index 71%
rename from src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/fingerprints/StartActivityInitFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt
index b71636793..558b24eba 100644
--- a/src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/fingerprints/StartActivityInitFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt
@@ -1,26 +1,15 @@
-package app.revanced.patches.googlenews.misc.integrations.fingerprints
+package app.revanced.patches.googlenews.misc.extension.hooks
-import app.revanced.patches.googlenews.misc.integrations.fingerprints.StartActivityInitFingerprint.getApplicationContextIndex
-import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
+import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.util.getReference
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
-internal object StartActivityInitFingerprint : IntegrationsFingerprint(
- opcodes = listOf(
- Opcode.INVOKE_STATIC,
- Opcode.MOVE_RESULT,
- Opcode.CONST_4,
- Opcode.IF_EQZ,
- Opcode.CONST,
- Opcode.INVOKE_VIRTUAL,
- Opcode.IPUT_OBJECT,
- Opcode.IPUT_BOOLEAN,
- Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext().
- Opcode.MOVE_RESULT_OBJECT,
- ),
+private var getApplicationContextIndex = -1
+
+internal val startActivityInitHook = extensionHook(
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getReference()?.name == "getApplicationContext"
@@ -33,9 +22,20 @@ internal object StartActivityInitFingerprint : IntegrationsFingerprint(
as OneRegisterInstruction
moveResultInstruction.registerA
},
- customFingerprint = { methodDef, classDef ->
- methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
- },
) {
- private var getApplicationContextIndex = -1
+ opcodes(
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT,
+ Opcode.CONST_4,
+ Opcode.IF_EQZ,
+ Opcode.CONST,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.IPUT_OBJECT,
+ Opcode.IPUT_BOOLEAN,
+ Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext().
+ Opcode.MOVE_RESULT_OBJECT,
+ )
+ custom { methodDef, classDef ->
+ methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
+ }
}
diff --git a/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt
rename to patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt
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
new file mode 100644
index 000000000..6ddeb3e07
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.googlenews.misc.gms
+
+import app.revanced.patcher.fingerprint
+
+internal val magazinesActivityOnCreateFingerprint = fingerprint {
+ custom { methodDef, classDef ->
+ methodDef.name == "onCreate" && classDef.endsWith("/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
new file mode 100644
index 000000000..d03ce31e7
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt
@@ -0,0 +1,30 @@
+package app.revanced.patches.googlenews.misc.gms
+
+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
+
+@Suppress("unused")
+val gmsCoreSupportPatch = gmsCoreSupportPatch(
+ fromPackageName = MAGAZINES_PACKAGE_NAME,
+ toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
+ mainActivityOnCreateFingerprint = magazinesActivityOnCreateFingerprint,
+ extensionPatch = extensionPatch,
+ gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
+) {
+ // Remove version constraint,
+ // once https://github.com/ReVanced/revanced-patches/pull/3111#issuecomment-2240877277 is resolved.
+ compatibleWith(MAGAZINES_PACKAGE_NAME("5.108.0.644447823"))
+}
+
+private fun gmsCoreSupportResourcePatch(
+ gmsCoreVendorGroupIdOption: Option,
+) = gmsCoreSupportResourcePatch(
+ fromPackageName = MAGAZINES_PACKAGE_NAME,
+ toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
+ spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
+ gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
+)
diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/ExtensionPatch.kt
new file mode 100644
index 000000000..406d28fe4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/ExtensionPatch.kt
@@ -0,0 +1,5 @@
+package app.revanced.patches.googlephotos.misc.extension
+
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val extensionPatch = sharedExtensionPatch(homeActivityInitHook)
diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/fingerprints/HomeActivityInitFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Fingerprints.kt
similarity index 69%
rename from src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/fingerprints/HomeActivityInitFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Fingerprints.kt
index 1fb7bf440..84e6d5876 100644
--- a/src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/fingerprints/HomeActivityInitFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Fingerprints.kt
@@ -1,22 +1,15 @@
-package app.revanced.patches.googlephotos.misc.integrations.fingerprints
+package app.revanced.patches.googlephotos.misc.extension
-import app.revanced.patches.googlephotos.misc.integrations.fingerprints.HomeActivityInitFingerprint.getApplicationContextIndex
-import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
+import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.util.getReference
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
-internal object HomeActivityInitFingerprint : IntegrationsFingerprint(
- opcodes = listOf(
- Opcode.CONST_STRING,
- Opcode.INVOKE_STATIC,
- Opcode.MOVE_RESULT_OBJECT,
- Opcode.IF_NEZ,
- Opcode.INVOKE_VIRTUAL, // Calls getApplicationContext().
- Opcode.MOVE_RESULT_OBJECT,
- ),
+private var getApplicationContextIndex = -1
+
+internal val homeActivityInitHook = extensionHook(
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getReference()?.name == "getApplicationContext"
@@ -29,9 +22,16 @@ internal object HomeActivityInitFingerprint : IntegrationsFingerprint(
as OneRegisterInstruction
moveResultInstruction.registerA
},
- customFingerprint = { methodDef, classDef ->
- methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;")
- },
) {
- private var getApplicationContextIndex = -1
+ opcodes(
+ Opcode.CONST_STRING,
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.IF_NEZ,
+ 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
new file mode 100644
index 000000000..95f2a3dba
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt
@@ -0,0 +1,7 @@
+package app.revanced.patches.googlephotos.misc.features
+
+import app.revanced.patcher.fingerprint
+
+internal val initializeFeaturesEnumFingerprint = fingerprint {
+ strings("com.google.android.apps.photos.NEXUS_PRELOAD")
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatch.kt
new file mode 100644
index 000000000..fa7ff3c14
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatch.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.googlephotos.misc.features
+
+import app.revanced.patches.all.misc.build.BuildInfo
+import app.revanced.patches.all.misc.build.baseSpoofBuildInfoPatch
+
+// Spoof build info to Google Pixel XL.
+@Suppress("unused")
+val spoofBuildInfoPatch = baseSpoofBuildInfoPatch {
+ BuildInfo(
+ brand = "google",
+ manufacturer = "Google",
+ device = "marlin",
+ product = "marlin",
+ model = "Pixel XL",
+ fingerprint = "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys",
+ )
+}
diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt
similarity index 53%
rename from src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt
index 040012d45..f0aaad890 100644
--- a/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt
@@ -1,30 +1,26 @@
-package app.revanced.patches.googlephotos.features
+package app.revanced.patches.googlephotos.misc.features
-import app.revanced.patcher.data.BytecodeContext
-import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringArrayPatchOption
-import app.revanced.patches.googlephotos.features.fingerprints.InitializeFeaturesEnumFingerprint
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patcher.patch.stringsOption
import app.revanced.util.getReference
-import app.revanced.util.resultOrThrow
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
-@Patch(
+@Suppress("unused")
+val spoofFeaturesPatch = bytecodePatch(
name = "Spoof features",
description = "Spoofs the device to enable Google Pixel exclusive features, including unlimited storage.",
- dependencies = [SpoofBuildInfoPatch::class],
- compatiblePackages = [CompatiblePackage("com.google.android.apps.photos")],
-)
-@Suppress("unused")
-object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprint)) {
- private val featuresToEnable by stringArrayPatchOption(
- "featuresToEnable",
- arrayOf(
+) {
+ compatibleWith("com.google.android.apps.photos")
+
+ dependsOn(spoofBuildInfoPatch)
+
+ val featuresToEnable by stringsOption(
+ key = "featuresToEnable",
+ default = listOf(
"com.google.android.apps.photos.NEXUS_PRELOAD",
"com.google.android.apps.photos.nexus_preload",
),
@@ -33,9 +29,9 @@ object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprin
required = true,
)
- private val featuresToDisable by stringArrayPatchOption(
- "featuresToDisable",
- arrayOf(
+ val featuresToDisable by stringsOption(
+ key = "featuresToDisable",
+ default = listOf(
"com.google.android.apps.photos.PIXEL_2017_PRELOAD",
"com.google.android.apps.photos.PIXEL_2018_PRELOAD",
"com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD",
@@ -58,29 +54,32 @@ object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprin
required = true,
)
- override fun execute(context: BytecodeContext) {
+ val initializeFeaturesEnumMatch by initializeFeaturesEnumFingerprint()
+
+ execute {
+ @Suppress("NAME_SHADOWING")
val featuresToEnable = featuresToEnable!!.toSet()
+
+ @Suppress("NAME_SHADOWING")
val featuresToDisable = featuresToDisable!!.toSet()
- InitializeFeaturesEnumFingerprint.resultOrThrow().let { result ->
- result.mutableMethod.apply {
- getInstructions().filter { it.opcode == Opcode.CONST_STRING }.forEach {
- val feature = it.getReference()!!.string
+ initializeFeaturesEnumMatch.mutableMethod.apply {
+ instructions.filter { it.opcode == Opcode.CONST_STRING }.forEach {
+ val feature = it.getReference()!!.string
- val spoofedFeature = when (feature) {
- in featuresToEnable -> "android.hardware.wifi"
- in featuresToDisable -> "dummy"
- else -> return@forEach
- }
-
- val constStringIndex = it.location.index
- val constStringRegister = (it as OneRegisterInstruction).registerA
-
- replaceInstruction(
- constStringIndex,
- "const-string v$constStringRegister, \"$spoofedFeature\"",
- )
+ val spoofedFeature = when (feature) {
+ in featuresToEnable -> "android.hardware.wifi"
+ in featuresToDisable -> "dummy"
+ else -> return@forEach
}
+
+ val constStringIndex = it.location.index
+ val constStringRegister = (it as OneRegisterInstruction).registerA
+
+ replaceInstruction(
+ constStringIndex,
+ "const-string v$constStringRegister, \"$spoofedFeature\"",
+ )
}
}
}
diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt
rename to patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt
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
new file mode 100644
index 000000000..f47c1a3d9
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.googlephotos.misc.gms
+
+import app.revanced.patcher.fingerprint
+
+internal val homeActivityOnCreateFingerprint = fingerprint {
+ custom { methodDef, classDef ->
+ methodDef.name == "onCreate" && classDef.endsWith("/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
new file mode 100644
index 000000000..3ed14a29d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt
@@ -0,0 +1,27 @@
+package app.revanced.patches.googlephotos.misc.gms
+
+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
+import app.revanced.patches.googlephotos.misc.gms.Constants.REVANCED_PHOTOS_PACKAGE_NAME
+import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
+
+@Suppress("unused")
+val gmsCoreSupportPatch = gmsCoreSupportPatch(
+ fromPackageName = PHOTOS_PACKAGE_NAME,
+ toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
+ mainActivityOnCreateFingerprint = homeActivityOnCreateFingerprint,
+ extensionPatch = extensionPatch,
+ gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
+) {
+ compatibleWith(PHOTOS_PACKAGE_NAME)
+}
+
+private fun gmsCoreSupportResourcePatch(
+ gmsCoreVendorGroupIdOption: Option,
+) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
+ fromPackageName = PHOTOS_PACKAGE_NAME,
+ toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
+ spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
+ gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
+)
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
new file mode 100644
index 000000000..54c20a7f8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 000000000..91693d047
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt
@@ -0,0 +1,27 @@
+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 com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
+ name = "Restore hidden 'Back up while charging' toggle",
+ description = "Restores a hidden toggle to only run backups when the device is charging.",
+) {
+ compatibleWith("com.google.android.apps.photos")
+
+ val backupPreferencesMatch by backupPreferencesFingerprint()
+
+ execute {
+ // Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
+ val chargingPrefStringIndex = backupPreferencesMatch.stringMatches!!.first().index
+ backupPreferencesMatch.mutableMethod.apply {
+ // Get the register of move-result.
+ val resultRegister = getInstruction(chargingPrefStringIndex + 2).registerA
+ // Insert const after move-result to override register as true.
+ addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 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
new file mode 100644
index 000000000..62e1e5f16
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt
@@ -0,0 +1,12 @@
+package app.revanced.patches.googlerecorder.restrictions
+
+import app.revanced.patcher.fingerprint
+
+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;")
+ }
+}
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
new file mode 100644
index 000000000..2c5a0aec1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt
@@ -0,0 +1,31 @@
+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.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val removeDeviceRestrictionsPatch = bytecodePatch(
+ name = "Remove device restrictions",
+ description = "Removes restrictions from using the app on any device. Requires mounting patched app over original.",
+) {
+ compatibleWith("com.google.android.apps.recorder")
+
+ val onApplicationCreateMatch by onApplicationCreateFingerprint()
+
+ execute {
+ val featureStringIndex = onApplicationCreateMatch.stringMatches!!.first().index
+
+ onApplicationCreateMatch.mutableMethod.apply {
+ // Remove check for device restrictions.
+ removeInstructions(featureStringIndex - 2, 5)
+
+ val featureAvailableRegister = getInstruction(featureStringIndex).registerA
+
+ // Override "isPixelDevice()" to return true.
+ addInstruction(featureStringIndex, "const/4 v$featureAvailableRegister, 0x1")
+ }
+ }
+}
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
new file mode 100644
index 000000000..ae269f817
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.hexeditor.ad
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableAdsPatch = bytecodePatch(
+ name = "Disable ads",
+) {
+ compatibleWith("com.myprog.hexedit")
+
+ val primaryAdsMatch by primaryAdsFingerprint()
+
+ execute {
+ primaryAdsMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..2fa2c5b85
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.hexeditor.ad
+
+import app.revanced.patcher.fingerprint
+
+internal val primaryAdsFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("PreferencesHelper;") && method.name == "isAdsDisabled"
+ }
+}
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
new file mode 100644
index 000000000..84db55457
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt
@@ -0,0 +1,8 @@
+package app.revanced.patches.iconpackstudio.misc.pro
+
+import app.revanced.patcher.fingerprint
+
+internal val checkProFingerprint = fingerprint {
+ returns("Z")
+ custom { _, classDef -> classDef.endsWith("IPSPurchaseRepository;") }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..daea6581c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.iconpackstudio.misc.pro
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockProPatch = bytecodePatch(
+ name = "Unlock pro",
+) {
+ compatibleWith("ginlemon.iconpackstudio"("2.2 build 016"))
+
+ val checkProMatch by checkProFingerprint()
+
+ execute {
+ checkProMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/Fingerprints.kt
new file mode 100644
index 000000000..38f814f6d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/Fingerprints.kt
@@ -0,0 +1,31 @@
+package app.revanced.patches.idaustria.detection.root
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val attestationSupportedCheckFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("V")
+ custom { method, classDef ->
+ method.name == "attestationSupportCheck" &&
+ classDef.endsWith("/DeviceIntegrityCheck;")
+ }
+}
+
+internal val bootloaderCheckFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("Z")
+ custom { method, classDef ->
+ method.name == "bootloaderCheck" &&
+ classDef.endsWith("/DeviceIntegrityCheck;")
+ }
+}
+
+internal val rootCheckFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("V")
+ custom { method, classDef ->
+ method.name == "rootCheck" &&
+ classDef.endsWith("/DeviceIntegrityCheck;")
+ }
+}
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
new file mode 100644
index 000000000..9440e20d3
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt
@@ -0,0 +1,20 @@
+package app.revanced.patches.idaustria.detection.root
+
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.util.returnEarly
+
+@Suppress("unused")
+val rootDetectionPatch = bytecodePatch(
+ name = "Remove root detection",
+ description = "Removes the check for root permissions and unlocked bootloader.",
+) {
+ compatibleWith("at.gv.oe.app")
+
+ attestationSupportedCheckFingerprint()
+ bootloaderCheckFingerprint()
+ rootCheckFingerprint()
+
+ execute {
+ setOf(attestationSupportedCheckFingerprint, bootloaderCheckFingerprint, rootCheckFingerprint).returnEarly(true)
+ }
+}
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
new file mode 100644
index 000000000..61cd9605f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt
@@ -0,0 +1,13 @@
+package app.revanced.patches.idaustria.detection.signature
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val spoofSignatureFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE)
+ returns("L")
+ parameters("L")
+ custom { method, classDef ->
+ classDef.endsWith("/SL2Step1Task;") && method.name == "getPubKey"
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt
similarity index 65%
rename from src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt
index ae427b744..12295f90c 100644
--- a/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt
@@ -1,23 +1,20 @@
package app.revanced.patches.idaustria.detection.signature
-import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patches.idaustria.detection.signature.fingerprints.SpoofSignatureFingerprint
+import app.revanced.patcher.patch.bytecodePatch
-@Patch(
+@Suppress("unused")
+val spoofSignaturePatch = bytecodePatch(
name = "Spoof signature",
description = "Spoofs the signature of the app.",
- compatiblePackages = [CompatiblePackage("at.gv.oe.app")]
-)
-@Suppress("unused")
-object SpoofSignaturePatch : BytecodePatch(
- setOf(SpoofSignatureFingerprint)
) {
- private const val EXPECTED_SIGNATURE =
- "OpenSSLRSAPublicKey{modulus=ac3e6fd6050aa7e0d6010ae58190404cd89a56935b44f6fee" +
+ compatibleWith("at.gv.oe.app")
+
+ val spoofSignatureMatch by spoofSignatureFingerprint()
+
+ execute {
+ val expectedSignature =
+ "OpenSSLRSAPublicKey{modulus=ac3e6fd6050aa7e0d6010ae58190404cd89a56935b44f6fee" +
"067c149768320026e10b24799a1339e414605e448e3f264444a327b9ae292be2b62ad567dd1800dbed4a88f718a33dc6db6b" +
"f5178aa41aa0efff8a3409f5ca95dbfccd92c7b4298966df806ea7a0204a00f0e745f6d9f13bdf24f3df715d7b62c1600906" +
"15de1c8a956b9286764985a3b3c060963c435fb9481a5543aaf0671fc2dba6c5c2b17d1ef1d85137f14dc9bbdf3490288087" +
@@ -29,13 +26,12 @@ object SpoofSignaturePatch : BytecodePatch(
"77ef1be61b2c01ebdabddcbf53cc4b6fd9a3c445606ee77b3758162c80ad8f8137b3c6864e92db904807dcb2be9d7717dd21" +
"bf42c121d620ddfb7914f7a95c713d9e1c1b7bdb4a03d618e40cf7e9e235c0b5687e03b7ab3,publicExponent=10001}"
- override fun execute(context: BytecodeContext) {
- SpoofSignatureFingerprint.result!!.mutableMethod.addInstructions(
+ spoofSignatureMatch.mutableMethod.addInstructions(
0,
"""
- const-string v0, "$EXPECTED_SIGNATURE"
+ const-string v0, "$expectedSignature"
return-object v0
- """
+ """,
)
}
}
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
new file mode 100644
index 000000000..573bd72e3
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt
@@ -0,0 +1,8 @@
+package app.revanced.patches.inshorts.ad
+
+import app.revanced.patcher.fingerprint
+
+internal val inshortsAdsFingerprint = fingerprint {
+ returns("V")
+ strings("GoogleAdLoader", "exception in requestAd")
+}
\ 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
new file mode 100644
index 000000000..3fff6a270
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt
@@ -0,0 +1,22 @@
+package app.revanced.patches.inshorts.ad
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideAdsPatch = bytecodePatch(
+ name = "Hide ads",
+) {
+ compatibleWith("com.nis.app")
+
+ val inshortsAdsMatch by inshortsAdsFingerprint()
+
+ execute {
+ inshortsAdsMatch.mutableMethod.addInstruction(
+ 0,
+ """
+ return-void
+ """,
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/Fingerprints.kt
new file mode 100644
index 000000000..65d052729
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/Fingerprints.kt
@@ -0,0 +1,14 @@
+package app.revanced.patches.instagram.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",
+ "SponsoredContentController::Delivery",
+ )
+}
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
new file mode 100644
index 000000000..1291c4212
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt
@@ -0,0 +1,25 @@
+package app.revanced.patches.instagram.ads
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideAdsPatch = bytecodePatch(
+ name = "Hide ads",
+ description = "Hides ads in stories, discover, profile, etc. " +
+ "An ad can still appear once when refreshing the home feed.",
+) {
+ compatibleWith("com.instagram.android")
+
+ val adInjectorMatch by adInjectorFingerprint()
+
+ execute {
+ adInjectorMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x0
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..30242b8d9
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.irplus.ad
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val irplusAdsFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ parameters("L", "Z")
+ strings("TAGGED")
+}
\ No newline at end of file
diff --git a/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt
new file mode 100644
index 000000000..a0a6e0170
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt
@@ -0,0 +1,19 @@
+package app.revanced.patches.irplus.ad
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val removeAdsPatch = bytecodePatch(
+ name = "Remove ads",
+) {
+ compatibleWith("net.binarymode.android.irplus")
+
+ val irplusAdsMatch by irplusAdsFingerprint()
+
+ execute {
+ // By overwriting the second parameter of the method,
+ // the view which holds the advertisement is removed.
+ irplusAdsMatch.mutableMethod.addInstruction(0, "const/4 p2, 0x0")
+ }
+}
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
new file mode 100644
index 000000000..79d41368e
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.lightroom.misc.login
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableMandatoryLoginPatch = bytecodePatch(
+ name = "Disable mandatory login",
+) {
+ compatibleWith("com.adobe.lrmobile")
+
+ val isLoggedInMatch by isLoggedInFingerprint()
+
+ execute {
+ isLoggedInMatch.mutableMethod.apply {
+ val index = implementation!!.instructions.lastIndex - 1
+ // Set isLoggedIn = true.
+ 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
new file mode 100644
index 000000000..6345541e1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.lightroom.misc.login
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val isLoggedInFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL)
+ returns("Z")
+ opcodes(
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.SGET_OBJECT,
+ Opcode.IF_NE,
+ Opcode.CONST_4,
+ Opcode.GOTO
+ )
+}
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
new file mode 100644
index 000000000..5a00dc68c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.lightroom.misc.premium
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val hasPurchasedFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
+ returns("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
new file mode 100644
index 000000000..730c523d4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.lightroom.misc.premium
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockPremiumPatch = bytecodePatch(
+ name = "Unlock premium",
+) {
+ compatibleWith("com.adobe.lrmobile")
+
+ val hasPurchasedMatch by hasPurchasedFingerprint()
+
+ execute {
+ // Set hasPremium = true.
+ hasPurchasedMatch.mutableMethod.replaceInstruction(2, "const/4 v2, 0x1")
+ }
+}
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
new file mode 100644
index 000000000..0dfbf5cda
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.memegenerator.detection.license
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val licenseValidationFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Z")
+ parameters("Landroid/content/Context;")
+ opcodes(
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT_WIDE,
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT_WIDE,
+ Opcode.CMP_LONG,
+ Opcode.IF_GEZ,
+ Opcode.CONST_4,
+ Opcode.RETURN,
+ Opcode.CONST_4,
+ Opcode.RETURN
+ )
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..6d49ef6a9
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.memegenerator.detection.license
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val licenseValidationPatch = bytecodePatch(
+ description = "Disables Firebase license validation.",
+) {
+ val licenseValidationMatch by licenseValidationFingerprint()
+
+ execute {
+ licenseValidationMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const/4 p0, 0x1
+ return p0
+ """,
+ )
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/fingerprints/VerifySignatureFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt
similarity index 54%
rename from src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/fingerprints/VerifySignatureFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt
index b77fd051f..75912318b 100644
--- a/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/fingerprints/VerifySignatureFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt
@@ -1,17 +1,14 @@
-package app.revanced.patches.memegenerator.detection.signature.fingerprints
+package app.revanced.patches.memegenerator.detection.signature
-import app.revanced.patcher.extensions.or
-import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
-import app.revanced.patcher.fingerprint.MethodFingerprint
-import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
-@FuzzyPatternScanMethod(2)
-internal object VerifySignatureFingerprint : MethodFingerprint(
- returnType = "Z",
- accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
- parameters = listOf("Landroid/app/Activity;"),
- opcodes = listOf(
+internal val verifySignatureFingerprint = fingerprint(fuzzyPatternScanThreshold = 2) {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Z")
+ parameters("Landroid/app/Activity;")
+ opcodes(
Opcode.SGET_OBJECT,
Opcode.IF_NEZ,
Opcode.INVOKE_STATIC,
@@ -31,5 +28,5 @@ internal object VerifySignatureFingerprint : MethodFingerprint(
Opcode.CONST_4,
Opcode.RETURN,
Opcode.ADD_INT_LIT8
- ),
-)
\ No newline at end of file
+ )
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..6637b57e7
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.memegenerator.detection.signature
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val signatureVerificationPatch = bytecodePatch(
+ description = "Disables detection of incorrect signature.",
+) {
+ val verifySignatureMatch by verifySignatureFingerprint()
+
+ execute {
+ verifySignatureMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const/4 p0, 0x1
+ return p0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..1f16bb10e
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.memegenerator.misc.pro
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val isFreeVersionFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Ljava/lang/Boolean;")
+ parameters("Landroid/content/Context;")
+ opcodes(
+ Opcode.SGET,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.CONST_STRING,
+ Opcode.INVOKE_VIRTUAL,
+ 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
new file mode 100644
index 000000000..fa475fbbc
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt
@@ -0,0 +1,27 @@
+package app.revanced.patches.memegenerator.misc.pro
+
+import app.revanced.patcher.extensions.InstructionExtensions.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",
+) {
+ dependsOn(signatureVerificationPatch, licenseValidationPatch)
+
+ compatibleWith("com.zombodroid.MemeGenerator"("4.6364", "4.6370", "4.6375", "4.6377"))
+
+ val isFreeVersionMatch by isFreeVersionFingerprint()
+
+ execute {
+ isFreeVersionMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ sget-object p0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;
+ return-object p0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..a2fa6329c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt
@@ -0,0 +1,36 @@
+package app.revanced.patches.messenger.inbox
+
+import app.revanced.patcher.fingerprint
+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")
+ 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"
+ }
+ }
+}
+
+internal val loadInboxAdsFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("V")
+ strings(
+ "ads_load_begin",
+ "inbox_ads_fetch_start",
+ )
+ 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
new file mode 100644
index 000000000..ca13b71c8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.messenger.inbox
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideInboxAdsPatch = bytecodePatch(
+ name = "Hide inbox ads",
+ description = "Hides ads in inbox.",
+) {
+ compatibleWith("com.facebook.orca")
+
+ val loadInboxAdsMatch by loadInboxAdsFingerprint()
+
+ execute {
+ loadInboxAdsMatch.mutableMethod.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
new file mode 100644
index 000000000..24d3f8192
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.messenger.inbox
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideInboxSubtabsPatch = bytecodePatch(
+ name = "Hide inbox subtabs",
+ description = "Hides Home and Channels tabs between active now tray and chats.",
+) {
+ compatibleWith("com.facebook.orca")
+
+ val createInboxSubTabsMatch by createInboxSubTabsFingerprint()
+
+ execute {
+ createInboxSubTabsMatch.mutableMethod.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
new file mode 100644
index 000000000..3d4e223f6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt
@@ -0,0 +1,26 @@
+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
+
+@Suppress("unused")
+val disableSwitchingEmojiToStickerPatch = bytecodePatch(
+ name = "Disable switching emoji to sticker",
+ description = "Disables switching from emoji to sticker search mode in message input field.",
+) {
+ compatibleWith("com.facebook.orca"("439.0.0.29.119"))
+
+ val switchMessangeInputEmojiButtonMatch by switchMessangeInputEmojiButtonFingerprint()
+
+ execute {
+ val setStringIndex = switchMessangeInputEmojiButtonMatch.patternMatch!!.startIndex + 2
+
+ switchMessangeInputEmojiButtonMatch.mutableMethod.apply {
+ 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
new file mode 100644
index 000000000..651491ca6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt
@@ -0,0 +1,18 @@
+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")
+
+ val sendTypingIndicatorMatch by sendTypingIndicatorFingerprint()
+
+ execute {
+ sendTypingIndicatorMatch.mutableMethod.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
new file mode 100644
index 000000000..0a1d181d2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt
@@ -0,0 +1,31 @@
+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 switchMessangeInputEmojiButtonFingerprint = 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/mifitness/misc/locale/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt
new file mode 100644
index 000000000..14105f762
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt
@@ -0,0 +1,12 @@
+package app.revanced.patches.mifitness.misc.locale
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val syncBluetoothLanguageFingerprint = fingerprint {
+ 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
new file mode 100644
index 000000000..bb038f26b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt
@@ -0,0 +1,33 @@
+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.patch.bytecodePatch
+import app.revanced.patches.mifitness.misc.login.fixLoginPatch
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val forceEnglishLocalePatch = bytecodePatch(
+ name = "Force English locale",
+ description = "Forces wearable devices to use the English locale.",
+) {
+ compatibleWith("com.xiaomi.wearable")
+
+ dependsOn(fixLoginPatch)
+
+ val syncBluetoothLanguageMatch by syncBluetoothLanguageFingerprint()
+
+ execute {
+ val resolvePhoneLocaleInstruction = syncBluetoothLanguageMatch.patternMatch!!.startIndex
+
+ syncBluetoothLanguageMatch.mutableMethod.apply {
+ val registerIndexToUpdate =
+ getInstruction(resolvePhoneLocaleInstruction).registerA
+
+ 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
new file mode 100644
index 000000000..e3eee2499
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt
@@ -0,0 +1,12 @@
+package app.revanced.patches.mifitness.misc.login
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val xiaomiAccountManagerConstructorFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
+ parameters("Landroid/content/Context;", "Z")
+ custom { method, _ ->
+ method.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
new file mode 100644
index 000000000..27c18eae0
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.mifitness.misc.login
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val fixLoginPatch = bytecodePatch(
+ name = "Fix login",
+ description = "Fixes login for uncertified Mi Fitness app",
+) {
+ compatibleWith("com.xiaomi.wearable")
+
+ val xiaomiAccountManagerConstructorMatch by xiaomiAccountManagerConstructorFingerprint()
+
+ execute {
+ xiaomiAccountManagerConstructorMatch.mutableMethod.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
new file mode 100644
index 000000000..6ce0519ad
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt
@@ -0,0 +1,13 @@
+package app.revanced.patches.music.ad.video
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val showVideoAdsParentFingerprint = fingerprint {
+ 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
new file mode 100644
index 000000000..b39b2822f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt
@@ -0,0 +1,22 @@
+package app.revanced.patches.music.ad.video
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideVideoAdsPatch = bytecodePatch(
+ name = "Hide music video ads",
+ description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.",
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val showVideoAdsParentMatch by showVideoAdsParentFingerprint()
+
+ execute { context ->
+ val showVideoAdsMethod = context
+ .navigate(showVideoAdsParentMatch.mutableMethod)
+ .at(showVideoAdsParentMatch.patternMatch!!.startIndex + 1).mutable()
+
+ showVideoAdsMethod.addInstruction(0, "const/4 p1, 0x0")
+ }
+}
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
new file mode 100644
index 000000000..6d09557c8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt
@@ -0,0 +1,26 @@
+package app.revanced.patches.music.audio.exclusiveaudio
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val enableExclusiveAudioPlaybackPatch = bytecodePatch(
+ name = "Enable exclusive audio playback",
+ description = "Enables the option to play audio without video.",
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val allowExclusiveAudioPlaybackMatch by allowExclusiveAudioPlaybackFingerprint()
+
+ execute {
+ allowExclusiveAudioPlaybackMatch.mutableMethod.apply {
+ addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/fingerprints/AllowExclusiveAudioPlaybackFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt
similarity index 55%
rename from src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/fingerprints/AllowExclusiveAudioPlaybackFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt
index c86f79427..02a978f0f 100644
--- a/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/fingerprints/AllowExclusiveAudioPlaybackFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt
@@ -1,15 +1,14 @@
-package app.revanced.patches.music.audio.exclusiveaudio.fingerprints
+package app.revanced.patches.music.audio.exclusiveaudio
-import app.revanced.patcher.extensions.or
-import app.revanced.patcher.fingerprint.MethodFingerprint
-import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
-internal object AllowExclusiveAudioPlaybackFingerprint: MethodFingerprint(
- "Z",
- AccessFlags.PUBLIC or AccessFlags.FINAL,
- listOf(),
- listOf(
+internal val allowExclusiveAudioPlaybackFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ parameters()
+ opcodes(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
@@ -22,4 +21,4 @@ internal object AllowExclusiveAudioPlaybackFingerprint: MethodFingerprint(
Opcode.MOVE_RESULT,
Opcode.RETURN
)
-)
\ No newline at end of file
+}
\ 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
new file mode 100644
index 000000000..13820d29d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt
@@ -0,0 +1,20 @@
+package app.revanced.patches.music.interaction.permanentrepeat
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val repeatTrackFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("V")
+ parameters("L", "L")
+ opcodes(
+ Opcode.IGET_OBJECT,
+ Opcode.IGET_OBJECT,
+ Opcode.SGET_OBJECT,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.MOVE_RESULT,
+ 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
new file mode 100644
index 000000000..31c3be9d6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt
@@ -0,0 +1,31 @@
+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.patch.bytecodePatch
+import app.revanced.patcher.util.smali.ExternalLabel
+import org.stringtemplate.v4.compiler.Bytecode.instructions
+
+@Suppress("unused")
+val permanentRepeatPatch = bytecodePatch(
+ name = "Permanent repeat",
+ description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
+ use = false,
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val repeatTrackMatch by repeatTrackFingerprint()
+
+ execute {
+ val startIndex = repeatTrackMatch.patternMatch!!.endIndex
+ val repeatIndex = startIndex + 1
+
+ repeatTrackMatch.mutableMethod.apply {
+ addInstructionsWithLabels(
+ startIndex,
+ "goto :repeat",
+ ExternalLabel("repeat", instructions[repeatIndex]),
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..f2169744f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt
@@ -0,0 +1,19 @@
+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
new file mode 100644
index 000000000..f4d754216
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt
@@ -0,0 +1,28 @@
+package app.revanced.patches.music.interaction.permanentshuffle
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val permanentShufflePatch = bytecodePatch(
+ name = "Permanent shuffle",
+ description = "Permanently remember your shuffle preference " +
+ "even if the playlist ends or another track is played.",
+ use = false,
+) {
+ compatibleWith(
+ "com.google.android.apps.youtube.music"(
+ "6.45.54",
+ "6.51.53",
+ "7.01.53",
+ "7.02.52",
+ "7.03.52",
+ ),
+ )
+
+ val disableShuffleMatch by disableShuffleFingerprint()
+
+ execute {
+ disableShuffleMatch.mutableMethod.addInstruction(0, "return-void")
+ }
+}
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
new file mode 100644
index 000000000..d7f0f03de
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt
@@ -0,0 +1,22 @@
+package app.revanced.patches.music.layout.compactheader
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val constructCategoryBarFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ parameters("Landroid/content/Context;", "L", "L", "L")
+ opcodes(
+ Opcode.IPUT_OBJECT,
+ Opcode.CONST,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.IPUT_OBJECT,
+ Opcode.CONST,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.NEW_INSTANCE,
+ Opcode.INVOKE_DIRECT,
+ )
+}
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
new file mode 100644
index 000000000..0dc7e02e4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt
@@ -0,0 +1,32 @@
+package app.revanced.patches.music.layout.compactheader
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
+import app.revanced.patcher.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val hideCategoryBar = bytecodePatch(
+ name = "Hide category bar",
+ description = "Hides the category bar at the top of the homepage.",
+ use = false,
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val constructCategoryBarMatch by constructCategoryBarFingerprint()
+
+ execute {
+ constructCategoryBarMatch.mutableMethod.apply {
+ val insertIndex = constructCategoryBarMatch.patternMatch!!.startIndex
+ val register = getInstruction(insertIndex - 1).registerA
+
+ addInstructions(
+ insertIndex,
+ """
+ const/16 v2, 0x8
+ invoke-virtual {v$register, v2}, Landroid/view/View;->setVisibility(I)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
new file mode 100644
index 000000000..29558ab4b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt
@@ -0,0 +1,31 @@
+package app.revanced.patches.music.layout.premium
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val hideGetPremiumFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("V")
+ parameters()
+ opcodes(
+ Opcode.IF_NEZ,
+ Opcode.CONST_16,
+ Opcode.INVOKE_VIRTUAL,
+ )
+ strings("FEmusic_history", "FEmusic_offline")
+}
+
+internal val membershipSettingsFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Ljava/lang/CharSequence;")
+ opcodes(
+ Opcode.IGET_OBJECT,
+ Opcode.INVOKE_INTERFACE,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.IF_EQZ,
+ Opcode.IGET_OBJECT,
+ )
+}
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
new file mode 100644
index 000000000..08711e1fb
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt
@@ -0,0 +1,48 @@
+package app.revanced.patches.music.layout.premium
+
+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.patcher.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
+
+@Suppress("unused")
+val hideGetPremiumPatch = bytecodePatch(
+ name = "Hide 'Get Music Premium' label",
+ description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val hideGetPremiumMatch by hideGetPremiumFingerprint()
+ val membershipSettingsMatch by membershipSettingsFingerprint()
+
+ execute {
+ hideGetPremiumMatch.mutableMethod.apply {
+ val insertIndex = hideGetPremiumMatch.patternMatch!!.endIndex
+
+ val setVisibilityInstruction = getInstruction(insertIndex)
+ val getPremiumViewRegister = setVisibilityInstruction.registerC
+ val visibilityRegister = setVisibilityInstruction.registerD
+
+ replaceInstruction(
+ insertIndex,
+ "const/16 v$visibilityRegister, 0x8",
+ )
+
+ addInstruction(
+ insertIndex + 1,
+ "invoke-virtual {v$getPremiumViewRegister, v$visibilityRegister}, " +
+ "Landroid/view/View;->setVisibility(I)V",
+ )
+ }
+
+ membershipSettingsMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x0
+ return-object v0
+ """,
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt
new file mode 100644
index 000000000..f3c96dc44
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.music.layout.upgradebutton
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val pivotBarConstructorFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ parameters("L", "Z")
+ opcodes(
+ Opcode.CHECK_CAST,
+ Opcode.INVOKE_INTERFACE,
+ Opcode.GOTO,
+ Opcode.IPUT_OBJECT,
+ Opcode.RETURN_VOID
+ )
+}
\ No newline at end of file
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt
new file mode 100644
index 000000000..01167b6e4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt
@@ -0,0 +1,77 @@
+package app.revanced.patches.music.layout.upgradebutton
+
+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.patcher.extensions.newLabel
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patcher.util.smali.toInstructions
+import app.revanced.util.getReference
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t
+import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
+import com.android.tools.smali.dexlib2.iface.reference.FieldReference
+
+@Suppress("unused")
+val removeUpgradeButtonPatch = bytecodePatch(
+ name = "Remove upgrade button",
+ description = "Removes the upgrade tab from the pivot bar.",
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val pivotBarConstructorMatch by pivotBarConstructorFingerprint()
+
+ execute {
+ pivotBarConstructorMatch.mutableMethod.apply {
+ val pivotBarElementFieldReference =
+ getInstruction(pivotBarConstructorMatch.patternMatch!!.endIndex - 1)
+ .getReference()
+
+ val register = getInstruction(0).registerC
+
+ // First compile all the needed instructions.
+ val instructionList = """
+ invoke-interface { v0 }, Ljava/util/List;->size()I
+ move-result v1
+ const/4 v2, 0x4
+ invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
+ iput-object v0, v$register, $pivotBarElementFieldReference
+ """.toInstructions().toMutableList()
+
+ val endIndex = pivotBarConstructorMatch.patternMatch!!.endIndex
+
+ // Replace the instruction to retain the label at given index.
+ replaceInstruction(
+ endIndex - 1,
+ instructionList[0], // invoke-interface.
+ )
+ // Do not forget to remove this instruction since we added it already.
+ instructionList.removeFirst()
+
+ val exitInstruction = instructionList.last() // iput-object
+ addInstruction(
+ endIndex,
+ exitInstruction,
+ )
+ // Do not forget to remove this instruction since we added it already.
+ instructionList.removeLast()
+
+ // Add the necessary if statement to remove the upgrade tab button in case it exists.
+ instructionList.add(
+ 2, // if-le.
+ BuilderInstruction22t(
+ Opcode.IF_LE,
+ 1,
+ 2,
+ newLabel(endIndex),
+ ),
+ )
+
+ addInstructions(
+ endIndex,
+ instructionList,
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..b499cdbc7
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.music.misc.androidauto
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val bypassCertificateChecksPatch = bytecodePatch(
+ name = "Bypass certificate checks",
+ description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val checkCertificateMatch by checkCertificateFingerprint()
+
+ execute {
+ checkCertificateMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..957f055b6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.music.misc.androidauto
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val checkCertificateFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ parameters("Ljava/lang/String;")
+ strings("X509", "Failed to get certificate.")
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..7e791af12
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt
@@ -0,0 +1,31 @@
+package app.revanced.patches.music.misc.backgroundplayback
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val backgroundPlaybackPatch = bytecodePatch(
+ name = "Remove background playback restrictions",
+ description = "Removes restrictions on background playback, including playing kids videos in the background.",
+) {
+ compatibleWith("com.google.android.apps.youtube.music")
+
+ val kidsBackgroundPlaybackPolicyControllerMatch by kidsBackgroundPlaybackPolicyControllerFingerprint()
+ val backgroundPlaybackDisableMatch by backgroundPlaybackDisableFingerprint()
+
+ execute {
+ kidsBackgroundPlaybackPolicyControllerMatch.mutableMethod.addInstruction(
+ 0,
+ "return-void",
+ )
+
+ backgroundPlaybackDisableMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..e1cf24e1a
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt
@@ -0,0 +1,42 @@
+package app.revanced.patches.music.misc.backgroundplayback
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val backgroundPlaybackDisableFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Z")
+ parameters("L")
+ opcodes(
+ Opcode.CONST_4,
+ Opcode.IF_EQZ,
+ Opcode.IGET,
+ Opcode.AND_INT_LIT16,
+ Opcode.IF_EQZ,
+ Opcode.IGET_OBJECT,
+ Opcode.IF_NEZ,
+ Opcode.SGET_OBJECT,
+ Opcode.IGET,
+ )
+}
+
+internal val kidsBackgroundPlaybackPolicyControllerFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("V")
+ parameters("I", "L", "Z")
+ opcodes(
+ Opcode.IGET,
+ Opcode.IF_NE,
+ Opcode.IGET_OBJECT,
+ Opcode.IF_NE,
+ Opcode.IGET_BOOLEAN,
+ Opcode.IF_EQ,
+ Opcode.GOTO,
+ Opcode.RETURN_VOID,
+ Opcode.SGET_OBJECT,
+ Opcode.CONST_4,
+ Opcode.IF_NE,
+ Opcode.IPUT_BOOLEAN,
+ )
+}
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
new file mode 100644
index 000000000..e6c1c69fe
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt
@@ -0,0 +1,6 @@
+package app.revanced.patches.music.misc.extension
+
+import app.revanced.patches.music.misc.extension.hooks.applicationInitHook
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch(applicationInitHook)
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
new file mode 100644
index 000000000..1e1a43f9a
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt
@@ -0,0 +1,10 @@
+package app.revanced.patches.music.misc.extension.hooks
+
+import app.revanced.patches.shared.misc.extension.extensionHook
+
+internal val applicationInitHook = extensionHook {
+ returns("V")
+ parameters()
+ strings("activity")
+ custom { method, _ -> method.name == "onCreate" }
+}
diff --git a/src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt
rename to patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt
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
new file mode 100644
index 000000000..7131e143d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.music.misc.gms
+
+import app.revanced.patcher.fingerprint
+
+internal val musicActivityOnCreateFingerprint = fingerprint {
+ returns("V")
+ parameters("Landroid/os/Bundle;")
+ custom { method, classDef ->
+ method.name == "onCreate" && classDef.endsWith("/MusicActivity;")
+ }
+}
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
new file mode 100644
index 000000000..e38ca6015
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt
@@ -0,0 +1,33 @@
+package app.revanced.patches.music.misc.gms
+
+import app.revanced.patcher.patch.Option
+import app.revanced.patches.music.misc.extension.sharedExtensionPatch
+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.shared.castContextFetchFingerprint
+import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
+import app.revanced.patches.shared.primeMethodFingerprint
+
+@Suppress("unused")
+val gmsCoreSupportPatch = gmsCoreSupportPatch(
+ fromPackageName = MUSIC_PACKAGE_NAME,
+ toPackageName = REVANCED_MUSIC_PACKAGE_NAME,
+ primeMethodFingerprint = primeMethodFingerprint,
+ earlyReturnFingerprints = setOf(
+ castContextFetchFingerprint,
+ ),
+ mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint,
+ extensionPatch = sharedExtensionPatch,
+ gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
+) {
+ compatibleWith(MUSIC_PACKAGE_NAME)
+}
+
+private fun gmsCoreSupportResourcePatch(
+ gmsCoreVendorGroupIdOption: Option,
+) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
+ fromPackageName = MUSIC_PACKAGE_NAME,
+ toPackageName = REVANCED_MUSIC_PACKAGE_NAME,
+ gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
+ spoofedPackageSignature = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875",
+)
diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/AbstractClientIdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/AbstractClientIdFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt
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
new file mode 100644
index 000000000..6bc4c21e5
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt
@@ -0,0 +1,8 @@
+package app.revanced.patches.myexpenses.misc.pro
+
+import app.revanced.patcher.fingerprint
+
+internal val isEnabledFingerprint = fingerprint {
+ returns("Z")
+ strings("feature", "feature.licenceStatus")
+}
\ 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
new file mode 100644
index 000000000..6f3fd4a1b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.myexpenses.misc.pro
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockProPatch = bytecodePatch(
+ name = "Unlock pro",
+) {
+ compatibleWith("org.totschnig.myexpenses")
+
+ val isEnabledMatch by isEnabledFingerprint()
+
+ execute {
+ isEnabledMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..160e2db27
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt
@@ -0,0 +1,19 @@
+package app.revanced.patches.myfitnesspal.ads
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val isPremiumUseCaseImplFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ custom { method, classDef ->
+ classDef.endsWith("IsPremiumUseCaseImpl;") && method.name == "doWork"
+ }
+}
+
+internal val mainActivityNavigateToNativePremiumUpsellFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
+ returns("V")
+ custom { method, classDef ->
+ classDef.endsWith("MainActivity;") && method.name == "navigateToNativePremiumUpsell"
+ }
+}
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
new file mode 100644
index 000000000..47be5c2fd
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt
@@ -0,0 +1,33 @@
+package app.revanced.patches.myfitnesspal.ads
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideAdsPatch = bytecodePatch(
+ name = "Hide ads",
+ description = "Hides most of the ads across the app.",
+) {
+ compatibleWith("com.myfitnesspal.android")
+
+ val isPremiumUseCaseImplMatch by isPremiumUseCaseImplFingerprint()
+ val mainActivityNavigateToNativePremiumUpsellMatch by mainActivityNavigateToNativePremiumUpsellFingerprint()
+
+ execute {
+ // Overwrite the premium status specifically for ads.
+ isPremiumUseCaseImplMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;
+ return-object v0
+ """,
+ )
+
+ // 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.
+ mainActivityNavigateToNativePremiumUpsellMatch.mutableMethod.replaceInstructions(
+ 0,
+ "return-void",
+ )
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt
similarity index 62%
rename from src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt
index 70346ec2c..c747f426e 100644
--- a/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt
@@ -1,23 +1,17 @@
package app.revanced.patches.netguard.broadcasts.removerestriction
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.resourcePatch
import org.w3c.dom.Element
-@Patch(
+@Suppress("unused")
+val removeBroadcastsRestrictionPatch = resourcePatch(
name = "Remove broadcasts restriction",
description = "Enables starting/stopping NetGuard via broadcasts.",
- compatiblePackages = [CompatiblePackage("eu.faircode.netguard")],
- use = false,
-)
-@Suppress("unused")
-object RemoveBroadcastsRestrictionPatch : ResourcePatch() {
- override fun execute(context: ResourceContext) {
- context.xmlEditor["AndroidManifest.xml"].use { editor ->
- val document = editor.file
+) {
+ compatibleWith("eu.faircode.netguard")
+ execute { context ->
+ context.document["AndroidManifest.xml"].use { document ->
val applicationNode =
document
.getElementsByTagName("application")
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
new file mode 100644
index 000000000..f4f5d48c0
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt
@@ -0,0 +1,10 @@
+package app.revanced.patches.nfctoolsse.misc.pro
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+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
new file mode 100644
index 000000000..c44f91116
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.nfctoolsse.misc.pro
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockProPatch = bytecodePatch(
+ name = "Unlock pro",
+) {
+ compatibleWith("com.wakdev.apps.nfctools.se")
+
+ val isLicenseRegisteredMatch by isLicenseRegisteredFingerprint()
+
+ execute {
+ isLicenseRegisteredMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..e2bffa451
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 000000000..a8a1f3a18
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.nyx.misc.pro
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockProPatch = bytecodePatch(
+ name = "Unlock pro",
+) {
+ compatibleWith("com.awedea.nyx")
+
+ val checkProMatch by checkProFingerprint()
+
+ execute {
+ checkProMatch.mutableMethod.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
new file mode 100644
index 000000000..69463c510
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt
@@ -0,0 +1,12 @@
+package app.revanced.patches.openinghours.misc.fix.crash
+
+import app.revanced.patcher.fingerprint
+
+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;"
+ }
+}
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
new file mode 100644
index 000000000..a24ba416f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt
@@ -0,0 +1,107 @@
+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.newLabel
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.util.getReference
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t
+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.reference.MethodReference
+
+@Suppress("unused")
+val fixCrashPatch = bytecodePatch(
+ name = "Fix crash",
+) {
+ compatibleWith("de.simon.openinghours"("1.0"))
+
+ val setPlaceMatch by setPlaceFingerprint()
+
+ execute {
+ val indexedInstructions = setPlaceMatch.mutableMethod.instructions.withIndex().toList()
+
+ /**
+ * This function replaces all `checkNotNull` instructions in the integer interval
+ * from [startIndex] to [endIndex], both inclusive. In place of the `checkNotNull`
+ * instruction an if-null check is inserted. If the if-null check yields that
+ * the value is indeed null, we jump to a newly created label at `endIndex + 1`.
+ */
+ fun avoidNullPointerException(startIndex: Int, endIndex: Int) {
+ val continueLabel = setPlaceMatch.mutableMethod.newLabel(endIndex + 1)
+
+ for (index in startIndex..endIndex) {
+ val instruction = indexedInstructions[index].value
+
+ if (!instruction.isCheckNotNullInstruction) {
+ continue
+ }
+
+ val checkNotNullInstruction = instruction as FiveRegisterInstruction
+ val originalRegister = checkNotNullInstruction.registerC
+
+ setPlaceMatch.mutableMethod.replaceInstruction(
+ index,
+ BuilderInstruction21t(
+ Opcode.IF_EQZ,
+ originalRegister,
+ continueLabel,
+ ),
+ )
+ }
+ }
+
+ val getOpeningHoursIndex = getIndicesOfInvoke(
+ indexedInstructions,
+ "Lde/simon/openinghours/models/Place;",
+ "getOpeningHours",
+ )
+
+ val setWeekDayTextIndex = getIndexOfInvoke(
+ indexedInstructions,
+ "Lde/simon/openinghours/views/custom/PlaceCard;",
+ "setWeekDayText",
+ )
+
+ val startCalculateStatusIndex = getIndexOfInvoke(
+ indexedInstructions,
+ "Lde/simon/openinghours/views/custom/PlaceCard;",
+ "startCalculateStatus",
+ )
+
+ // Replace the Intrinsics;->checkNotNull instructions with a null check
+ // and jump to our newly created label if it returns true.
+ // This avoids the NullPointerExceptions.
+ avoidNullPointerException(getOpeningHoursIndex[1], startCalculateStatusIndex)
+ avoidNullPointerException(getOpeningHoursIndex[0], setWeekDayTextIndex)
+ }
+}
+
+private fun isInvokeInstruction(instruction: Instruction, className: String, methodName: String): Boolean {
+ val methodRef = instruction.getReference() ?: return false
+ return methodRef.definingClass == className && methodRef.name == methodName
+}
+
+private fun getIndicesOfInvoke(
+ instructions: List>,
+ className: String,
+ methodName: String,
+): List = instructions.mapNotNull { (index, instruction) ->
+ if (isInvokeInstruction(instruction, className, methodName)) {
+ index
+ } else {
+ null
+ }
+}
+
+private fun getIndexOfInvoke(
+ instructions: List>,
+ className: String,
+ methodName: String,
+): Int = instructions.first { (_, instruction) ->
+ isInvokeInstruction(instruction, className, methodName)
+}.index
+
+private val Instruction.isCheckNotNullInstruction
+ get() = isInvokeInstruction(this, "Lkotlin/jvm/internal/Intrinsics;", "checkNotNull")
diff --git a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt
similarity index 55%
rename from src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt
index fe01d7b59..90c0bbb91 100644
--- a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt
@@ -1,11 +1,12 @@
-package app.revanced.patches.photomath.detection.deviceid.fingerprints
+package app.revanced.patches.photomath.detection.deviceid
-import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
+import app.revanced.patcher.fingerprint
-internal object GetDeviceIdFingerprint : MethodFingerprint(
- returnType = "Ljava/lang/String;",
- opcodes = listOf(
+internal val getDeviceIdFingerprint = fingerprint {
+ returns("Ljava/lang/String;")
+ parameters()
+ opcodes(
Opcode.SGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_STATIC,
@@ -16,6 +17,5 @@ internal object GetDeviceIdFingerprint : MethodFingerprint(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
- ),
- parameters = listOf()
-)
\ No newline at end of file
+ )
+}
\ 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
new file mode 100644
index 000000000..152ee8edd
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt
@@ -0,0 +1,28 @@
+package app.revanced.patches.photomath.detection.deviceid
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch
+import kotlin.random.Random
+
+@Suppress("unused")
+val getDeviceIdPatch = bytecodePatch(
+ name = "Spoof device ID",
+ description = "Spoofs device ID to mitigate manual bans by developers.",
+) {
+ dependsOn(signatureDetectionPatch)
+
+ compatibleWith("com.microblink.photomath"("8.37.0"))
+
+ val getDeviceIdMatch by getDeviceIdFingerprint()
+
+ execute {
+ getDeviceIdMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const-string v0, "${Random.nextLong().toString(16)}"
+ return-object v0
+ """,
+ )
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt
similarity index 52%
rename from src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt
index cd784ab20..5d7a20783 100644
--- a/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt
@@ -1,13 +1,10 @@
-package app.revanced.patches.photomath.detection.signature.fingerprints
+package app.revanced.patches.photomath.detection.signature
-import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
+import app.revanced.patcher.fingerprint
-internal object CheckSignatureFingerprint : MethodFingerprint(
- strings = listOf(
- "signatures",
- ),
- opcodes = listOf(
+internal val checkSignatureFingerprint = fingerprint {
+ opcodes(
Opcode.CONST_STRING,
Opcode.INVOKE_STATIC,
Opcode.INVOKE_STATIC,
@@ -17,4 +14,5 @@ internal object CheckSignatureFingerprint : MethodFingerprint(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
)
-)
+ strings("signatures")
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..5a2a7c5da
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt
@@ -0,0 +1,25 @@
+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.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val signatureDetectionPatch = bytecodePatch(
+ description = "Disables detection of incorrect signature.",
+) {
+ val checkSignatureMatch by checkSignatureFingerprint()
+
+ execute {
+ val signatureCheckInstruction = checkSignatureMatch.mutableMethod.getInstruction(
+ checkSignatureMatch.patternMatch!!.endIndex,
+ )
+ val checkRegister = (signatureCheckInstruction as OneRegisterInstruction).registerA
+
+ checkSignatureMatch.mutableMethod.replaceInstruction(
+ signatureCheckInstruction.location.index,
+ "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
new file mode 100644
index 000000000..301f2f9a5
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.photomath.misc.annoyances
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val hideUpdatePopupFingerprint = fingerprint {
+ accessFlags(AccessFlags.FINAL, AccessFlags.PUBLIC)
+ returns("V")
+ opcodes(
+ Opcode.CONST_HIGH16,
+ Opcode.INVOKE_VIRTUAL, // ViewPropertyAnimator.alpha(1.0f)
+ Opcode.MOVE_RESULT_OBJECT,
+ 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
new file mode 100644
index 000000000..00d0caa18
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.photomath.misc.annoyances
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch
+
+@Suppress("unused")
+val hideUpdatePopupPatch = bytecodePatch(
+ name = "Hide update popup",
+ description = "Prevents the update popup from showing up.",
+) {
+ dependsOn(signatureDetectionPatch)
+
+ compatibleWith("com.microblink.photomath"("8.32.0"))
+
+ val hideUpdatePopupMatch by hideUpdatePopupFingerprint()
+
+ execute {
+ hideUpdatePopupMatch.mutableMethod.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
new file mode 100644
index 000000000..adf0d7bd8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.photomath.misc.unlock.bookpoint
+
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val enableBookpointPatch = bytecodePatch(
+ description = "Enables textbook access",
+) {
+ val isBookpointEnabledMatch by isBookpointEnabledFingerprint()
+
+ execute {
+ isBookpointEnabledMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..6722f4223
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt
@@ -0,0 +1,16 @@
+package app.revanced.patches.photomath.misc.unlock.bookpoint
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val isBookpointEnabledFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ parameters()
+ strings(
+ "NoGeoData",
+ "NoCountryInGeo",
+ "RemoteConfig",
+ "GeoRCMismatch"
+ )
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..f6c282cbd
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt
@@ -0,0 +1,13 @@
+package app.revanced.patches.photomath.misc.unlock.plus
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val isPlusUnlockedFingerprint = fingerprint{
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ strings("genius")
+ custom { _, classDef ->
+ classDef.endsWith("/User;")
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..f62533e0c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt
@@ -0,0 +1,27 @@
+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
+
+@Suppress("unused")
+val unlockPlusPatch = bytecodePatch(
+ name = "Unlock plus",
+) {
+ dependsOn(signatureDetectionPatch, enableBookpointPatch)
+
+ compatibleWith("com.microblink.photomath"("8.37.0"))
+
+ val isPlusUnlockedMatch by isPlusUnlockedFingerprint()
+
+ execute {
+ isPlusUnlockedMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..8c2d579ef
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.piccomafr.misc
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val getAndroidIdFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Ljava/lang/String;")
+ parameters("Landroid/content/Context;")
+ strings("context", "android_id")
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..6d9e4dc5c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt
@@ -0,0 +1,52 @@
+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
+
+@Suppress("unused")
+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.",
+ use = false,
+) {
+ compatibleWith(
+ "com.piccomaeurope.fr"(
+ "6.4.0",
+ "6.4.1",
+ "6.4.2",
+ "6.4.3",
+ "6.4.4",
+ "6.4.5",
+ "6.5.0",
+ "6.5.1",
+ "6.5.2",
+ "6.5.3",
+ "6.5.4",
+ "6.6.0",
+ "6.6.1",
+ "6.6.2",
+ ),
+ )
+
+ val getAndroidIDMatch by getAndroidIdFingerprint()
+
+ val androidDeviceId by stringOption(
+ key = "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 {
+ getAndroidIDMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const-string v0, "$androidDeviceId"
+ return-object v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..cf4ba8b08
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt
@@ -0,0 +1,71 @@
+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.patch.bytecodePatch
+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.StringReference
+
+@Suppress("unused")
+val disableTrackingPatch = bytecodePatch(
+ name = "Disable tracking",
+ description = "Disables tracking by replacing tracking URLs with example.com.",
+) {
+ compatibleWith(
+ "com.piccomaeurope.fr"(
+ "6.4.0",
+ "6.4.1",
+ "6.4.2",
+ "6.4.3",
+ "6.4.4",
+ "6.4.5",
+ "6.5.0",
+ "6.5.1",
+ "6.5.2",
+ "6.5.3",
+ "6.5.4",
+ "6.6.0",
+ "6.6.1",
+ "6.6.2",
+ ),
+ )
+
+ val facebookSDKMatch by facebookSDKFingerprint()
+ val firebaseInstallMatch by firebaseInstallFingerprint()
+ val appMeasurementMatch by appMeasurementFingerprint()
+
+ execute {
+ facebookSDKMatch.mutableMethod.apply {
+ instructions.filter { instruction ->
+ instruction.opcode == Opcode.CONST_STRING
+ }.forEach { instruction ->
+ instruction as OneRegisterInstruction
+
+ replaceInstruction(
+ instruction.location.index,
+ "const-string v${instruction.registerA}, \"example.com\"",
+ )
+ }
+ }
+
+ firebaseInstallMatch.mutableMethod.apply {
+ 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\"",
+ )
+ }
+ }
+
+ appMeasurementMatch.mutableMethod.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
new file mode 100644
index 000000000..794f21bcb
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.piccomafr.tracking
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val appMeasurementFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
+ returns("V")
+ strings("config/app/", "Fetching remote configuration")
+}
+
+internal val facebookSDKFingerprint = fingerprint {
+ accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ strings("instagram.com", "facebook.com")
+}
+
+internal val firebaseInstallFingerprint = fingerprint {
+ 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
new file mode 100644
index 000000000..3e2addaa0
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt
@@ -0,0 +1,12 @@
+package app.revanced.patches.pixiv.ads
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val shouldShowAdsFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ custom { methodDef, classDef ->
+ classDef.type.endsWith("AdUtils;") && methodDef.name == "shouldShowAds"
+ }
+}
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
new file mode 100644
index 000000000..584d2f48a
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.pixiv.ads
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideAdsPatch = bytecodePatch(
+ name = "Hide ads",
+) {
+ compatibleWith("jp.pxv.android")
+
+ val shouldShowAdsMatch by shouldShowAdsFingerprint()
+
+ execute {
+ shouldShowAdsMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x0
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..a4d2c9e22
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt
@@ -0,0 +1,12 @@
+package app.revanced.patches.rar.misc.annoyances.purchasereminder
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val showReminderFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("V")
+ custom { method, _ ->
+ method.definingClass.endsWith("AdsNotify;") && method.name == "show"
+ }
+}
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
new file mode 100644
index 000000000..32957d5bd
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt
@@ -0,0 +1,19 @@
+package app.revanced.patches.rar.misc.annoyances.purchasereminder
+
+import app.revanced.patcher.extensions.InstructionExtensions.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")
+
+ val showReminderMatch by showReminderFingerprint()
+
+ execute {
+ showReminderMatch.mutableMethod.addInstruction(0, "return-void")
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt
similarity index 65%
rename from src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt
index 311803195..f3f6d6669 100644
--- a/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt
@@ -1,20 +1,18 @@
package app.revanced.patches.reddit.ad.banner
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.Patch
+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
-@Patch(description = "Hides banner ads from comments on subreddits.")
-object HideBannerPatch : ResourcePatch() {
- private const val RESOURCE_FILE_PATH = "res/layout/merge_listheader_link_detail.xml"
-
- override fun execute(context: ResourceContext) {
- context.xmlEditor[RESOURCE_FILE_PATH].use { editor ->
- val document = editor.file
+@Suppress("unused")
+val hideBannerPatch = resourcePatch(
+ description = "Hides banner ads from comments on subreddits.",
+) {
+ execute { context ->
+ val resourceFilePath = "res/layout/merge_listheader_link_detail.xml"
+ context.document[resourceFilePath].use { document ->
document.getElementsByTagName("merge").item(0).childNodes.apply {
val attributes = arrayOf("height", "width")
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
new file mode 100644
index 000000000..c99df5707
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt
@@ -0,0 +1,14 @@
+package app.revanced.patches.reddit.ad.comments
+
+import app.revanced.patcher.fingerprint
+
+internal val hideCommentAdsFingerprint = fingerprint {
+ strings(
+ "link",
+ // CommentPageRepository is not returning a link object
+ "is not returning a link object"
+ )
+ custom { _, classDef ->
+ classDef.sourceFile == "PostDetailPresenter.kt"
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..c556e76a2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt
@@ -0,0 +1,22 @@
+package app.revanced.patches.reddit.ad.comments
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val hideCommentAdsPatch = bytecodePatch(
+ description = "Removes ads in the comments.",
+) {
+ val hideCommentAdsMatch by hideCommentAdsFingerprint()
+
+ execute {
+ hideCommentAdsMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ new-instance v0, Ljava/lang/Object;
+ invoke-direct {v0}, Ljava/lang/Object;->()V
+ return-object v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..e7dd78912
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.reddit.ad.general
+
+import app.revanced.patcher.fingerprint
+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 newAdPostFingerprint = fingerprint {
+ opcodes(Opcode.INVOKE_VIRTUAL)
+ strings("chain", "feedElement")
+ custom { _, classDef -> classDef.sourceFile == "AdElementConverter.kt" }
+}
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
new file mode 100644
index 000000000..fae110042
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt
@@ -0,0 +1,81 @@
+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.patch.bytecodePatch
+import app.revanced.patches.reddit.ad.banner.hideBannerPatch
+import app.revanced.patches.reddit.ad.comments.hideCommentAdsPatch
+import app.revanced.patches.reddit.misc.extension.sharedExtensionPatch
+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
+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",
+) {
+ dependsOn(hideBannerPatch, hideCommentAdsPatch, sharedExtensionPatch)
+
+ // 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
+ // and https://github.com/iBotPeaches/Apktool/issues/3534.
+ // This constraint is necessary due to dependency on hideBannerPatch.
+ compatibleWith("com.reddit.frontpage"("2024.17.0"))
+
+ val adPostMatch by adPostFingerprint()
+ val newAdPostMatch by newAdPostFingerprint()
+
+ execute {
+ // 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;"
+
+ adPostMatch.mutableMethod.apply {
+ val setPostsListChildren = 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}
+ """,
+ )
+ }
+
+ // endregion
+
+ // region Remove ads from popular and latest feed
+
+ // The new feeds work by inserting posts into lists.
+ // AdElementConverter is conveniently responsible for inserting all feed ads.
+ // By removing the appending instruction no ad posts gets appended to the feed.
+ val index = newAdPostMatch.method.implementation!!.instructions.indexOfFirst {
+ if (it.opcode != Opcode.INVOKE_VIRTUAL) return@indexOfFirst false
+
+ val reference = (it as ReferenceInstruction).reference as MethodReference
+
+ reference.name == "add" && reference.definingClass == "Ljava/util/ArrayList;"
+ }
+
+ newAdPostMatch.mutableMethod.removeInstruction(index)
+ }
+
+ // endregion
+}
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
new file mode 100644
index 000000000..ccc0346a6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.reddit.customclients
+
+import app.revanced.patcher.patch.BytecodePatchBuilder
+import app.revanced.patcher.patch.Patch
+import app.revanced.patcher.patch.bytecodePatch
+
+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<*>,
+ block: BytecodePatchBuilder.() -> Unit = {},
+) = bytecodePatch(name = "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
new file mode 100644
index 000000000..b21b36a0d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt
@@ -0,0 +1,34 @@
+package app.revanced.patches.reddit.customclients
+
+import app.revanced.patcher.patch.BytecodePatchBuilder
+import app.revanced.patcher.patch.Option
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patcher.patch.stringOption
+
+/**
+ * Base class for patches that spoof the Reddit client.
+ *
+ * @param redirectUri The redirect URI of the Reddit OAuth client.
+ * @param block The patch block. It is called with the client ID option.
+ */
+fun spoofClientPatch(
+ redirectUri: String,
+ block: BytecodePatchBuilder.(Option) -> Unit = {},
+) = bytecodePatch(
+ name = "Spoof client",
+ description = "Restores functionality of the app by using custom client ID.",
+) {
+ block(
+ stringOption(
+ "client-id",
+ null,
+ null,
+ "OAuth client ID",
+ "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,
+ ),
+ )
+}
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
new file mode 100644
index 000000000..bb87c2114
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt
@@ -0,0 +1,19 @@
+package app.revanced.patches.reddit.customclients.baconreader.api
+
+import app.revanced.patcher.fingerprint
+
+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 requestTokenFingerprint = fingerprint {
+ strings("zACVn0dSFGdWqQ", "kDm2tYpu9DqyWFFyPlNcXGEni4k") // App ID and secret.
+}
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
new file mode 100644
index 000000000..649dffc16
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt
@@ -0,0 +1,40 @@
+package app.revanced.patches.reddit.customclients.baconreader.api
+
+import app.revanced.patcher.Match
+import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patches.reddit.customclients.spoofClientPatch
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val spoofClientPatch = spoofClientPatch(redirectUri = "http://baconreader.com/auth") { clientIdOption ->
+ compatibleWith(
+ "com.onelouder.baconreader",
+ "com.onelouder.baconreader.premium",
+ )
+
+ val getAuthorizationUrlMatch by getAuthorizationUrlFingerprint()
+ val requestTokenMatch by requestTokenFingerprint()
+
+ val clientId by clientIdOption
+
+ execute {
+ fun Match.patch(replacementString: String) {
+ val clientIdIndex = stringMatches!!.first().index
+
+ mutableMethod.apply {
+ val clientIdRegister = getInstruction(clientIdIndex).registerA
+ replaceInstruction(
+ clientIdIndex,
+ "const-string v$clientIdRegister, \"$replacementString\"",
+ )
+ }
+ }
+
+ // Patch client id in authorization url.
+ getAuthorizationUrlMatch.patch("client_id=$clientId")
+
+ // Patch client id for access token request.
+ requestTokenMatch.patch(clientId!!)
+ }
+}
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
new file mode 100644
index 000000000..fefe25160
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt
@@ -0,0 +1,20 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.ads
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableAdsPatch = bytecodePatch(
+ name = "Disable ads",
+) {
+ compatibleWith("com.rubenmayayo.reddit")
+
+ val maxMediationMatch by maxMediationFingerprint()
+ val admobMediationMatch by admobMediationFingerprint()
+
+ execute {
+ arrayOf(maxMediationMatch, admobMediationMatch).forEach {
+ it.mutableMethod.addInstructions(0, "return-void")
+ }
+ }
+}
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
new file mode 100644
index 000000000..618e2f145
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.ads
+
+import app.revanced.patcher.fingerprint
+
+internal val maxMediationFingerprint = fingerprint {
+ strings("MaxMediation: Attempting to initialize SDK")
+}
+
+internal val admobMediationFingerprint = fingerprint {
+ strings("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
new file mode 100644
index 000000000..cc06fd396
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt
@@ -0,0 +1,15 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.api
+
+import app.revanced.patcher.fingerprint
+
+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"
+ }
+}
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
new file mode 100644
index 000000000..fb4df92f2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt
@@ -0,0 +1,41 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.api
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patches.reddit.customclients.spoofClientPatch
+
+@Suppress("unused")
+val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption ->
+ compatibleWith("com.rubenmayayo.reddit")
+
+ val getClientIdMatch by getClientIdFingerprint()
+ val buildUserAgentMatch by buildUserAgentFingerprint()
+
+ val clientId by clientIdOption
+
+ execute {
+ // region Patch client id.
+
+ getClientIdMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const-string v0, "$clientId"
+ return-object v0
+ """,
+ )
+
+ // endregion
+
+ // region Patch user agent.
+
+ // Use a random number as the platform in the user agent string.
+ val platformName = (0..100000).random()
+ val platformParameter = 0
+
+ buildUserAgentMatch.mutableMethod.addInstructions(
+ 0,
+ "const-string p$platformParameter, \"$platformName\"",
+ )
+
+ // 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
new file mode 100644
index 000000000..a2b1530b8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt
@@ -0,0 +1,7 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads
+
+import app.revanced.patcher.fingerprint
+
+internal val downloadAudioFingerprint = fingerprint {
+ strings("/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
new file mode 100644
index 000000000..31975f44b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt
@@ -0,0 +1,32 @@
+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")
+
+ val downloadAudioMatch by downloadAudioFingerprint()
+
+ execute {
+ val endpointReplacements = mapOf(
+ "/DASH_audio.mp4" to "/DASH_AUDIO_128.mp4",
+ "/audio" to "/DASH_AUDIO_64.mp4",
+ )
+
+ downloadAudioMatch.stringMatches!!.forEach { match ->
+ downloadAudioMatch.mutableMethod.apply {
+ 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/slink/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt
new file mode 100644
index 000000000..665dba5a4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val getOAuthAccessTokenFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("Ljava/lang/String")
+ strings("access_token")
+ custom { method, _ -> method.definingClass == "Lnet/dean/jraw/http/oauth/OAuthData;" }
+}
+
+internal val handleNavigationFingerprint = fingerprint {
+ strings(
+ "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
new file mode 100644
index 000000000..b35a85320
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt
@@ -0,0 +1,53 @@
+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.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
+import app.revanced.patches.reddit.customclients.fixSLinksPatch
+
+const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/boostforreddit/FixSLinksPatch;"
+
+@Suppress("unused")
+val fixSlinksPatch = fixSLinksPatch(
+ extensionPatch = sharedExtensionPatch,
+) {
+ compatibleWith("com.rubenmayayo.reddit")
+
+ val handleNavigationMatch by handleNavigationFingerprint()
+ val setAccessTokenMatch by getOAuthAccessTokenFingerprint()
+
+ execute {
+ // region Patch navigation handler.
+
+ handleNavigationMatch.mutableMethod.apply {
+ val urlRegister = "p1"
+ val tempRegister = "v1"
+
+ 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.
+
+ setAccessTokenMatch.mutableMethod.addInstruction(
+ 3,
+ "invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->$SET_ACCESS_TOKEN_METHOD",
+ )
+
+ // endregion
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatch.kt
new file mode 100644
index 000000000..3d92d142b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatch.kt
@@ -0,0 +1,6 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.misc.extension
+
+import app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.hooks.initHook
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch(initHook)
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
new file mode 100644
index 000000000..1e7e4e2e8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.hooks
+
+import app.revanced.patches.shared.misc.extension.extensionHook
+
+internal val initHook = extensionHook(
+ insertIndexResolver = { 1 },
+) {
+ custom { method, _ ->
+ method.definingClass == "Lcom/rubenmayayo/reddit/MyApplication;" && method.name == "onCreate"
+ }
+}
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
new file mode 100644
index 000000000..4bce1362c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt
@@ -0,0 +1,7 @@
+package app.revanced.patches.reddit.customclients.infinityforreddit.api
+
+import app.revanced.patcher.fingerprint
+
+internal val apiUtilsFingerprint = fingerprint {
+ strings("native-lib")
+}
\ No newline at end of file
diff --git a/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
similarity index 60%
rename from src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt
index 0565bfefa..3ec7d8c69 100644
--- a/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,24 +1,22 @@
package app.revanced.patches.reddit.customclients.infinityforreddit.api
-import app.revanced.patcher.data.BytecodeContext
-import app.revanced.patcher.extensions.or
-import app.revanced.patcher.fingerprint.MethodFingerprintResult
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstructions
-import app.revanced.patches.reddit.customclients.BaseSpoofClientPatch
-import app.revanced.patches.reddit.customclients.infinityforreddit.api.fingerprints.APIUtilsFingerprint
+import app.revanced.patches.reddit.customclients.spoofClientPatch
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
@Suppress("unused")
-object SpoofClientPatch : BaseSpoofClientPatch(
- redirectUri = "infinity://localhost",
- clientIdFingerprints = setOf(APIUtilsFingerprint),
- compatiblePackages = setOf(CompatiblePackage("ml.docilealligator.infinityforreddit"))
-) {
- override fun Set.patchClientId(context: BytecodeContext) {
- first().mutableClass.methods.apply {
+val spoofClientPatch = spoofClientPatch(redirectUri = "infinity://localhost") { clientIdOption ->
+ compatibleWith("ml.docilealligator.infinityforreddit")
+
+ val apiUtilsMatch by apiUtilsFingerprint()
+
+ val clientId by clientIdOption
+
+ execute {
+ apiUtilsMatch.mutableClass.methods.apply {
val getClientIdMethod = single { it.name == "getId" }.also(::remove)
val newGetClientIdMethod = ImmutableMethod(
@@ -26,7 +24,7 @@ object SpoofClientPatch : BaseSpoofClientPatch(
getClientIdMethod.name,
null,
getClientIdMethod.returnType,
- AccessFlags.PUBLIC or AccessFlags.STATIC,
+ AccessFlags.PUBLIC.value or AccessFlags.STATIC.value,
null,
null,
ImmutableMethodImplementation(
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
new file mode 100644
index 000000000..36fe06279
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt
@@ -0,0 +1,15 @@
+package app.revanced.patches.reddit.customclients.infinityforreddit.subscription
+
+import app.revanced.patcher.fingerprint
+import app.revanced.util.literal
+
+internal val billingClientOnServiceConnectedFingerprint = fingerprint {
+ strings("Billing service connected")
+}
+
+internal val startSubscriptionActivityFingerprint = fingerprint {
+ literal {
+ // Intent start flag only used in the subscription activity
+ 0x10008000
+ }
+}
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
new file mode 100644
index 000000000..1c427c967
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt
@@ -0,0 +1,22 @@
+package app.revanced.patches.reddit.customclients.infinityforreddit.subscription
+
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.reddit.customclients.infinityforreddit.api.spoofClientPatch
+import app.revanced.util.returnEarly
+
+@Suppress("unused")
+val unlockSubscriptionPatch = bytecodePatch(
+ name = "Unlock subscription",
+ description = "Unlocks the subscription feature but requires a custom client ID.",
+) {
+ dependsOn(spoofClientPatch)
+
+ compatibleWith("ml.docilealligator.infinityforreddit")
+
+ startSubscriptionActivityFingerprint()
+ billingClientOnServiceConnectedFingerprint()
+
+ execute {
+ setOf(startSubscriptionActivityFingerprint, billingClientOnServiceConnectedFingerprint).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
new file mode 100644
index 000000000..c6498ee00
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt
@@ -0,0 +1,26 @@
+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
+
+@Suppress("unused")
+val disableAdsPatch = bytecodePatch(
+ name = "Disable ads",
+) {
+ dependsOn(disablePiracyDetectionPatch)
+
+ compatibleWith("o.o.joey")
+
+ val isAdFreeUserMatch by isAdFreeUserFingerprint()
+
+ execute {
+ isAdFreeUserMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..465faf120
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt
@@ -0,0 +1,10 @@
+package app.revanced.patches.reddit.customclients.joeyforreddit.ads
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val isAdFreeUserFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("Z")
+ strings("AD_FREE_USER")
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..e6c591748
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt
@@ -0,0 +1,28 @@
+package app.revanced.patches.reddit.customclients.joeyforreddit.api
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val authUtilityUserAgentFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Ljava/lang/String;")
+ opcodes(Opcode.APUT_OBJECT)
+ custom { method, classDef ->
+ classDef.sourceFile == "AuthUtility.java"
+ }
+}
+
+internal val getClientIdFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("L")
+ opcodes(
+ Opcode.CONST, // R.string.valuable_cid
+ Opcode.INVOKE_STATIC, // StringMaster.decrypt
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.RETURN_OBJECT
+ )
+ custom { _, classDef ->
+ classDef.sourceFile == "AuthUtility.java"
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..ad3fc0cb1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt
@@ -0,0 +1,52 @@
+package app.revanced.patches.reddit.customclients.joeyforreddit.api
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
+import app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy.disablePiracyDetectionPatch
+import app.revanced.patches.reddit.customclients.spoofClientPatch
+
+@Suppress("unused")
+val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/authorize_callback") { clientIdOption ->
+ dependsOn(disablePiracyDetectionPatch)
+
+ compatibleWith(
+ "o.o.joey",
+ "o.o.joey.pro",
+ "o.o.joey.dev",
+ )
+
+ val getClientIdMatch by getClientIdFingerprint()
+ val authUtilityUserAgentMatch by authUtilityUserAgentFingerprint()
+
+ val clientId by clientIdOption
+
+ execute {
+ // region Patch client id.
+
+ getClientIdMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const-string v0, "$clientId"
+ return-object v0
+ """,
+ )
+
+ // endregion
+
+ // region Patch user agent.
+
+ // Use a random user agent.
+ val randomName = (0..100000).random()
+ val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
+
+ authUtilityUserAgentMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const-string v0, "$userAgent"
+ return-object v0
+ """,
+ )
+
+ // 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
new file mode 100644
index 000000000..32a676f9d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt
@@ -0,0 +1,13 @@
+package app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disablePiracyDetectionPatch = bytecodePatch {
+ val piracyDetectionMatch by piracyDetectionFingerprint()
+
+ execute {
+ piracyDetectionMatch.mutableMethod.addInstruction(0, "return-void")
+ }
+}
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
new file mode 100644
index 000000000..76343a530
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val piracyDetectionFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
+ returns("V")
+ opcodes(
+ Opcode.NEW_INSTANCE,
+ Opcode.CONST_16,
+ Opcode.CONST_WIDE_16,
+ Opcode.INVOKE_DIRECT,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.RETURN_VOID
+ )
+ custom { _, classDef ->
+ classDef.endsWith("ProcessLifeCyleListener;")
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..14d0f4766
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt
@@ -0,0 +1,31 @@
+package app.revanced.patches.reddit.customclients.redditisfun.api
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+fun baseClientIdFingerprint(string: String) = fingerprint {
+ strings("yyOCBp.RHJhDKd", string)
+}
+
+internal val basicAuthorizationFingerprint = baseClientIdFingerprint(
+ string = "fJOxVwBUyo*=f:.patchClientId(context: BytecodeContext) {
+val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { clientIdOption ->
+ compatibleWith(
+ "com.andrewshu.android.reddit",
+ "com.andrewshu.android.redditdonation",
+ )
+
+ val buildAuthorizationStringMatch by buildAuthorizationStringFingerprint()
+ val basicAuthorizationMatch by basicAuthorizationFingerprint()
+ val getUserAgentMatch by getUserAgentFingerprint()
+
+ val clientId by clientIdOption
+
+ execute {
+ // region Patch client id.
+
/**
* Replaces a one register instruction with a const-string instruction
* at the index returned by [getReplacementIndex].
*
* @param string The string to replace the instruction with.
* @param getReplacementIndex A function that returns the index of the instruction to replace
- * using the [StringMatch] list from the [MethodFingerprintResult].
+ * using the [Match.StringMatch] list from the [Match].
*/
- fun MethodFingerprintResult.replaceWith(
+ fun Match.replaceWith(
string: String,
- getReplacementIndex: List.() -> Int,
+ getReplacementIndex: List.() -> Int,
) = mutableMethod.apply {
- val replacementIndex = scanResult.stringsScanResult!!.matches.getReplacementIndex()
+ val replacementIndex = stringMatches!!.getReplacementIndex()
val clientIdRegister = getInstruction(replacementIndex).registerA
replaceInstruction(replacementIndex, "const-string v$clientIdRegister, \"$string\"")
}
// Patch OAuth authorization.
- first().replaceWith(clientId!!) { first().index + 4 }
+ buildAuthorizationStringMatch.replaceWith(clientId!!) { first().index + 4 }
// Path basic authorization.
- last().replaceWith("$clientId:") { last().index + 7 }
- }
+ basicAuthorizationMatch.replaceWith("$clientId:") { last().index + 7 }
+
+ // endregion
+
+ // region Patch user agent.
- override fun Set.patchUserAgent(context: BytecodeContext) {
// Use a random user agent.
val randomName = (0..100000).random()
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
- first().mutableMethod.addInstructions(
+ getUserAgentMatch.mutableMethod.addInstructions(
0,
"""
const-string v0, "$userAgent"
return-object v0
""",
)
- }
- override fun Set.patchMiscellaneous(context: BytecodeContext) {
+ // endregion
+
+ // region Patch miscellaneous.
+
// Reddit messed up and does not append a redirect uri to the authorization url to old.reddit.com/login.
// Replace old.reddit.com with ssl.reddit.com to fix this.
- BuildAuthorizationStringFingerprint.result!!.mutableMethod.apply {
+ buildAuthorizationStringMatch.mutableMethod.apply {
val index = indexOfFirstInstructionOrThrow {
getReference()?.contains("old.reddit.com") == true
}
@@ -78,5 +83,7 @@ object SpoofClientPatch : BaseSpoofClientPatch(
"const-string v$targetRegister, \"https://ssl.reddit.com/api/v1/authorize.compact\"",
)
}
+
+ // endregion
}
}
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
new file mode 100644
index 000000000..263602f73
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt
@@ -0,0 +1,26 @@
+package app.revanced.patches.reddit.customclients.relayforreddit.api
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.Opcode
+
+internal fun baseClientIdFingerprint(string: String) = fingerprint {
+ strings("dj-xCIZQYiLbEg", string)
+}
+
+internal val getLoggedInBearerTokenFingerprint = baseClientIdFingerprint("authorization_code")
+
+internal val getLoggedOutBearerTokenFingerprint = baseClientIdFingerprint("https://oauth.reddit.com/grants/installed_client")
+
+internal val getRefreshTokenFingerprint = baseClientIdFingerprint("refresh_token")
+
+internal val loginActivityClientIdFingerprint = baseClientIdFingerprint("&duration=permanent")
+
+internal val redditCheckDisableAPIFingerprint = fingerprint {
+ opcodes(Opcode.IF_EQZ)
+ strings("Reddit Disabled")
+}
+
+internal val setRemoteConfigFingerprint = fingerprint {
+ parameters("Lcom/google/firebase/remoteconfig/FirebaseRemoteConfig;")
+ strings("reddit_oauth_url")
+}
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
new file mode 100644
index 000000000..3313c7ff7
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt
@@ -0,0 +1,65 @@
+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.patches.reddit.customclients.spoofClientPatch
+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
+
+@Suppress("unused")
+val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") {
+ compatibleWith(
+ "free.reddit.news",
+ "reddit.news",
+ )
+
+ val loginActivityClientIdMatch by loginActivityClientIdFingerprint()
+ val getLoggedInBearerTokenMatch by getLoggedInBearerTokenFingerprint()
+ val getLoggedOutBearerTokenMatch by getLoggedOutBearerTokenFingerprint()
+ val getRefreshTokenMatch by getRefreshTokenFingerprint()
+ val setRemoteConfigMatch by setRemoteConfigFingerprint()
+ val redditCheckDisableAPIMatch by redditCheckDisableAPIFingerprint()
+
+ val clientId by it
+
+ execute {
+ // region Patch client id.
+
+ setOf(
+ loginActivityClientIdMatch,
+ getLoggedInBearerTokenMatch,
+ getLoggedOutBearerTokenMatch,
+ getRefreshTokenMatch,
+ ).forEach { match ->
+ val clientIdIndex = match.stringMatches!!.first().index
+ match.mutableMethod.apply {
+ val clientIdRegister = getInstruction(clientIdIndex).registerA
+
+ match.mutableMethod.replaceInstruction(
+ clientIdIndex,
+ "const-string v$clientIdRegister, \"$clientId\"",
+ )
+ }
+ }
+
+ // endregion
+
+ // region Patch miscellaneous.
+
+ // Do not load remote config which disables OAuth login remotely.
+ setRemoteConfigMatch.mutableMethod.addInstructions(0, "return-void")
+
+ // Prevent OAuth login being disabled remotely.
+ val checkIsOAuthRequestIndex = redditCheckDisableAPIMatch.patternMatch!!.startIndex
+
+ redditCheckDisableAPIMatch.mutableMethod.apply {
+ val returnNextChain = getInstruction(checkIsOAuthRequestIndex).target
+ replaceInstruction(checkIsOAuthRequestIndex, BuilderInstruction10t(Opcode.GOTO, returnNextChain))
+ }
+
+ // endregion
+ }
+}
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
new file mode 100644
index 000000000..4ff8be461
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.reddit.customclients.slide.api
+
+import app.revanced.patcher.fingerprint
+
+internal val getClientIdFingerprint = fingerprint {
+ custom { method, classDef ->
+ if (!classDef.endsWith("Credentials;")) return@custom false
+
+ method.name == "getClientId"
+ }
+}
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
new file mode 100644
index 000000000..bfd687096
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.reddit.customclients.slide.api
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patches.reddit.customclients.spoofClientPatch
+
+@Suppress("unused")
+val spoofClientPatch = spoofClientPatch(redirectUri = "http://www.ccrama.me") { clientIdOption ->
+ compatibleWith("me.ccrama.redditslide")
+
+ val getClientIdMatch by getClientIdFingerprint()
+
+ val clientId by clientIdOption
+
+ execute {
+ getClientIdMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const-string v0, "$clientId"
+ return-object v0
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..d9b5b9fd4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.reddit.customclients.sync.ads
+
+import app.revanced.patcher.patch.BytecodePatchBuilder
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.util.returnEarly
+
+fun disableAdsPatch(block: BytecodePatchBuilder.() -> Unit = {}) = bytecodePatch(
+ name = "Disable ads",
+) {
+ isAdsEnabledFingerprint()
+
+ execute {
+ isAdsEnabledFingerprint.returnEarly()
+ }
+
+ 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
new file mode 100644
index 000000000..e055493bd
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt
@@ -0,0 +1,10 @@
+package app.revanced.patches.reddit.customclients.sync.ads
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val isAdsEnabledFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Z")
+ strings("SyncIapHelper")
+}
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
new file mode 100644
index 000000000..e87e2e7b4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.reddit.customclients.sync.detection.piracy
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disablePiracyDetectionPatch = bytecodePatch(
+ description = "Disables detection of modified versions.",
+) {
+ val piracyDetectionMatch by piracyDetectionFingerprint()
+
+ execute {
+ // Do not throw an error if the fingerprint is not resolved.
+ // This is fine because new versions of the target app do not need this patch.
+ piracyDetectionMatch.mutableMethod.addInstruction(0, "return-void")
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt
similarity index 57%
rename from src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt
index f7cbc8771..7dc955912 100644
--- a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt
@@ -1,22 +1,21 @@
-package app.revanced.patches.reddit.customclients.syncforreddit.detection.piracy.fingerprints
+package app.revanced.patches.reddit.customclients.sync.detection.piracy
-import app.revanced.patcher.extensions.or
-import app.revanced.patcher.fingerprint.MethodFingerprint
-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.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
-internal object PiracyDetectionFingerprint : MethodFingerprint(
- returnType = "V",
- accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
- opcodes = listOf(
+internal val piracyDetectionFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
+ returns("V")
+ opcodes(
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
- Opcode.INVOKE_VIRTUAL
- ),
- customFingerprint = { method, _ ->
+ Opcode.INVOKE_VIRTUAL,
+ )
+ custom { method, _ ->
method.implementation?.instructions?.any {
if (it.opcode != Opcode.NEW_INSTANCE) return@any false
@@ -25,4 +24,4 @@ internal object PiracyDetectionFingerprint : MethodFingerprint(
reference.toString() == "Lcom/github/javiersantos/piracychecker/PiracyChecker;"
} ?: false
}
-)
\ No newline at end of file
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatch.kt
new file mode 100644
index 000000000..ca74997aa
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatch.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.reddit.customclients.sync.syncforlemmy.ads
+
+import app.revanced.patches.reddit.customclients.sync.ads.disableAdsPatch
+import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch
+
+@Suppress("unused")
+val disableAdsPatch = disableAdsPatch {
+ dependsOn(disablePiracyDetectionPatch)
+
+ compatibleWith("com.laurencedawson.reddit_sync")
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatch.kt
new file mode 100644
index 000000000..e50158cdd
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatch.kt
@@ -0,0 +1,8 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.ads
+
+import app.revanced.patches.reddit.customclients.sync.ads.disableAdsPatch
+
+@Suppress("unused")
+val disableAdsPatch = disableAdsPatch {
+ compatibleWith("io.syncapps.lemmy_sync")
+}
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
new file mode 100644
index 000000000..85d51573c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt
@@ -0,0 +1,26 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.annoyances.startup
+
+import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableSyncForLemmyBottomSheetPatch = bytecodePatch(
+ name = "Disable Sync for Lemmy bottom sheet",
+ description = "Disables the bottom sheet at the startup that asks you to signup to \"Sync for Lemmy\".",
+) {
+ compatibleWith(
+ "com.laurencedawson.reddit_sync"("v23.06.30-13:39"),
+ "com.laurencedawson.reddit_sync.pro"(), // Version unknown.
+ "com.laurencedawson.reddit_sync.dev"(), // Version unknown.
+ )
+
+ val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint()
+
+ execute {
+ mainActivityOnCreateMatch.mutableMethod.apply {
+ val showBottomSheetIndex = implementation!!.instructions.lastIndex - 1
+
+ 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
new file mode 100644
index 000000000..21c788a89
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.annoyances.startup
+
+import app.revanced.patcher.fingerprint
+
+internal val mainActivityOnCreateFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("MainActivity;") && method.name == "onCreate"
+ }
+}
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
new file mode 100644
index 000000000..c7902b1f4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt
@@ -0,0 +1,19 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.api
+
+import app.revanced.patcher.fingerprint
+
+internal val getAuthorizationStringFingerprint = fingerprint {
+ strings("authorize.compact?client_id")
+}
+
+internal val getBearerTokenFingerprint = fingerprint {
+ strings("Basic")
+}
+
+internal val getUserAgentFingerprint = fingerprint {
+ strings("android:com.laurencedawson.reddit_sync")
+}
+
+internal val imgurImageAPIFingerprint = fingerprint {
+ strings("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
new file mode 100644
index 000000000..1d9aaa5a8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt
@@ -0,0 +1,94 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.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.patches.reddit.customclients.spoofClientPatch
+import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch
+import app.revanced.util.matchOrThrow
+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.*
+
+@Suppress("unused")
+val spoofClientPatch = spoofClientPatch(
+ redirectUri = "http://redditsync/auth",
+) { clientIdOption ->
+ dependsOn(disablePiracyDetectionPatch)
+
+ compatibleWith(
+ "com.laurencedawson.reddit_sync",
+ "com.laurencedawson.reddit_sync.pro",
+ "com.laurencedawson.reddit_sync.dev",
+ )
+
+ val imgurImageAPIMatch by imgurImageAPIFingerprint()
+ val getAuthorizationStringMatch by getAuthorizationStringFingerprint()
+ val getUserAgentMatch by getUserAgentFingerprint()
+
+ val clientId by clientIdOption
+
+ execute { context ->
+ // region Patch client id.
+
+ getBearerTokenFingerprint.apply {
+ match(context, getAuthorizationStringMatch.classDef)
+ }.matchOrThrow.mutableMethod.apply {
+ val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8))
+ addInstructions(
+ 0,
+ """
+ const-string v0, "Basic $auth"
+ return-object v0
+ """,
+ )
+ val occurrenceIndex =
+ getAuthorizationStringMatch.stringMatches!!.first().index
+
+ getAuthorizationStringMatch.mutableMethod.apply {
+ val authorizationStringInstruction = getInstruction(occurrenceIndex)
+ val targetRegister = (authorizationStringInstruction as OneRegisterInstruction).registerA
+ val reference = authorizationStringInstruction.reference as StringReference
+
+ val newAuthorizationUrl = reference.string.replace(
+ "client_id=.*?&".toRegex(),
+ "client_id=$clientId&",
+ )
+
+ replaceInstruction(
+ occurrenceIndex,
+ "const-string v$targetRegister, \"$newAuthorizationUrl\"",
+ )
+ }
+ }
+
+ // endregion
+
+ // region Patch user agent.
+
+ // Use a random user agent.
+ val randomName = (0..100000).random()
+ val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
+
+ imgurImageAPIMatch.mutableMethod.replaceInstruction(
+ 0,
+ """
+ const-string v0, "$userAgent"
+ return-object v0
+ """,
+ )
+
+ // endregion
+
+ // region Patch Imgur API URL.
+
+ val apiUrlIndex = getUserAgentMatch.stringMatches!!.first().index
+ getUserAgentMatch.mutableMethod.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/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatch.kt
new file mode 100644
index 000000000..67f02676f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatch.kt
@@ -0,0 +1,6 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.extension
+
+import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.hooks.initHook
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch(initHook)
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
new file mode 100644
index 000000000..00244b4df
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.hooks
+
+import app.revanced.patches.shared.misc.extension.extensionHook
+
+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;"
+ }
+}
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
new file mode 100644
index 000000000..f7287fcc3
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt
@@ -0,0 +1,13 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.slink
+
+import app.revanced.patcher.fingerprint
+
+internal val linkHelperOpenLinkFingerprint = fingerprint {
+ strings("Link title: ")
+}
+
+internal val setAuthorizationHeaderFingerprint = fingerprint {
+ returns("Ljava/util/HashMap;")
+ strings("Authorization", "bearer ")
+ custom { method, _ -> method.definingClass == "Lcom/laurencedawson/reddit_sync/singleton/a;" }
+}
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
new file mode 100644
index 000000000..80da3f6bc
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt
@@ -0,0 +1,57 @@
+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.patches.reddit.customclients.RESOLVE_S_LINK_METHOD
+import app.revanced.patches.reddit.customclients.SET_ACCESS_TOKEN_METHOD
+import app.revanced.patches.reddit.customclients.fixSLinksPatch
+import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.sharedExtensionPatch
+
+const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/syncforreddit/FixSLinksPatch;"
+
+@Suppress("unused")
+val fixSLinksPatch = fixSLinksPatch(
+ extensionPatch = sharedExtensionPatch,
+) {
+ compatibleWith(
+ "com.laurencedawson.reddit_sync",
+ "com.laurencedawson.reddit_sync.pro",
+ "com.laurencedawson.reddit_sync.dev",
+ )
+
+ val handleNavigationMatch by linkHelperOpenLinkFingerprint()
+ val setAccessTokenMatch by setAuthorizationHeaderFingerprint()
+
+ execute {
+ // region Patch navigation handler.
+
+ handleNavigationMatch.mutableMethod.apply {
+ 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)),
+ )
+ }
+
+ // endregion
+
+ // region Patch set access token.
+
+ setAccessTokenMatch.mutableMethod.addInstruction(
+ 0,
+ "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->$SET_ACCESS_TOKEN_METHOD",
+ )
+
+ // endregion
+ }
+}
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
new file mode 100644
index 000000000..4bac74de7
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt
@@ -0,0 +1,36 @@
+package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.user
+
+import app.revanced.patcher.fingerprint
+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 val oAuthFriendRequestFingerprint = userEndpointFingerprint(
+ "OAuthFriendRequest.java",
+)
+
+internal val oAuthUnfriendRequestFingerprint = userEndpointFingerprint(
+ "OAuthUnfriendRequest.java",
+)
+
+internal val oAuthUserIdRequestFingerprint = userEndpointFingerprint(
+ "OAuthUserIdRequest.java",
+)
+
+internal val oAuthUserInfoRequestFingerprint = userEndpointFingerprint(
+ "OAuthUserInfoRequest.java",
+)
+
+internal val oAuthSubredditInfoRequestConstructorFingerprint = userEndpointFingerprint(
+ "OAuthSubredditInfoRequest.java",
+ setOf(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR),
+)
+
+internal val oAuthSubredditInfoRequestHelperFingerprint = userEndpointFingerprint(
+ "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
new file mode 100644
index 000000000..a53007045
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt
@@ -0,0 +1,51 @@
+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.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(
+ name = "Use /user/ endpoint",
+ description = "Replaces the deprecated endpoint for viewing user profiles /u with /user, that used to fix a bug.",
+ use = false,
+
+) {
+ compatibleWith(
+ "com.laurencedawson.reddit_sync",
+ "com.laurencedawson.reddit_sync.pro",
+ "com.laurencedawson.reddit_sync.dev",
+ )
+
+ val oAuthFriendRequestMatch by oAuthFriendRequestFingerprint()
+ val oAuthSubredditInfoRequestConstructorMatch by oAuthSubredditInfoRequestConstructorFingerprint()
+ val oAuthSubredditInfoRequestHelperMatch by oAuthSubredditInfoRequestHelperFingerprint()
+ val oAuthUnfriendRequestMatch by oAuthUnfriendRequestFingerprint()
+ val oAuthUserIdRequestMatch by oAuthUserIdRequestFingerprint()
+ val oAuthUserInfoRequestMatch by oAuthUserInfoRequestFingerprint()
+
+ execute {
+ arrayOf(
+ oAuthFriendRequestMatch,
+ oAuthSubredditInfoRequestConstructorMatch,
+ oAuthSubredditInfoRequestHelperMatch,
+ oAuthUnfriendRequestMatch,
+ oAuthUserIdRequestMatch,
+ oAuthUserInfoRequestMatch,
+ ).map { it.stringMatches!!.first().index to it.mutableMethod }.forEach { (userPathStringIndex, method) ->
+ val userPathStringInstruction = method.getInstruction(userPathStringIndex)
+
+ val userPathStringRegister = userPathStringInstruction.registerA
+ val fixedUserPathString = userPathStringInstruction.getReference()!!
+ .string.replace("u/", "user/")
+
+ method.replaceInstruction(
+ userPathStringIndex,
+ "const-string v$userPathStringRegister, \"${fixedUserPathString}\"",
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/fingerprints/ParseRedditVideoNetworkResponseFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/Fingerprints.kt
similarity index 53%
rename from src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/fingerprints/ParseRedditVideoNetworkResponseFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/Fingerprints.kt
index 1a06cd54d..3123563ee 100644
--- a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/fingerprints/ParseRedditVideoNetworkResponseFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/Fingerprints.kt
@@ -1,16 +1,16 @@
-package app.revanced.patches.reddit.customclients.syncforreddit.fix.video.fingerprints
+package app.revanced.patches.reddit.customclients.syncforreddit.fix.video
-import app.revanced.patcher.fingerprint.MethodFingerprint
+import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
-internal object ParseRedditVideoNetworkResponseFingerprint : MethodFingerprint(
- opcodes = listOf(
+internal val parseRedditVideoNetworkResponseFingerprint = fingerprint {
+ opcodes(
Opcode.NEW_INSTANCE,
Opcode.IGET_OBJECT,
Opcode.INVOKE_DIRECT,
- Opcode.CONST_WIDE_32
- ),
- customFingerprint = { methodDef, classDef ->
+ Opcode.CONST_WIDE_32,
+ )
+ custom { methodDef, classDef ->
classDef.sourceFile == "RedditVideoRequest.java" && methodDef.name == "parseNetworkResponse"
}
-)
+}
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
new file mode 100644
index 000000000..a43e13fe6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt
@@ -0,0 +1,57 @@
+package app.revanced.patches.reddit.customclients.syncforreddit.fix.video
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.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
+
+private const val EXTENSION_CLASS_DESCRIPTOR =
+ "Lapp/revanced/extension/syncforreddit/FixRedditVideoDownloadPatch;"
+private const val GET_LINKS_METHOD = "getLinks([B)[Ljava/lang/String;"
+
+@Suppress("unused")
+val fixVideoDownloadsPatch = bytecodePatch(
+ name = "Fix video downloads",
+ description = "Fixes a bug in Sync's MPD parser resulting in only the audio-track being saved.",
+) {
+ dependsOn(sharedExtensionPatch)
+
+ compatibleWith(
+ "com.laurencedawson.reddit_sync",
+ "com.laurencedawson.reddit_sync.pro",
+ "com.laurencedawson.reddit_sync.dev",
+ )
+
+ val parseRedditVideoNetworkResponseMatch by parseRedditVideoNetworkResponseFingerprint()
+
+ execute {
+ val scanResult = parseRedditVideoNetworkResponseMatch.patternMatch!!
+ val newInstanceIndex = scanResult.startIndex
+ val invokeDirectIndex = scanResult.endIndex - 1
+
+ val buildResponseInstruction = parseRedditVideoNetworkResponseMatch.mutableMethod.getInstruction(invokeDirectIndex)
+
+ parseRedditVideoNetworkResponseMatch.mutableMethod.addInstructions(
+ newInstanceIndex + 1,
+ """
+ # Get byte array from response.
+ iget-object v2, p1, Lcom/android/volley/NetworkResponse;->data:[B
+
+ # Parse the videoUrl and audioUrl from the byte array.
+ invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->$GET_LINKS_METHOD
+ move-result-object v2
+
+ # Get videoUrl (Index 0).
+ const/4 v5, 0x0
+ aget-object v${buildResponseInstruction.registerE}, v2, v5
+
+ # Get audioUrl (Index 1).
+ const/4 v6, 0x1
+ aget-object v${buildResponseInstruction.registerF}, v2, v6
+
+ # Register E and F are used to build the response.
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..116205a34
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.reddit.layout.disablescreenshotpopup
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableScreenshotPopupPatch = bytecodePatch(
+ name = "Disable screenshot popup",
+ description = "Disables the popup that shows up when taking a screenshot.",
+) {
+ compatibleWith("com.reddit.frontpage")
+
+ val disableScreenshotPopupMatch by disableScreenshotPopupFingerprint()
+
+ execute {
+ disableScreenshotPopupMatch.mutableMethod.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
new file mode 100644
index 000000000..09fe16247
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt
@@ -0,0 +1,15 @@
+package app.revanced.patches.reddit.layout.disablescreenshotpopup
+
+import app.revanced.patcher.fingerprint
+
+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"
+ }
+}
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
new file mode 100644
index 000000000..2eac1cbe2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt
@@ -0,0 +1,10 @@
+package app.revanced.patches.reddit.layout.premiumicon
+
+import app.revanced.patcher.fingerprint
+
+internal val hasPremiumIconAccessFingerprint = fingerprint {
+ returns("Z")
+ custom { method, classDef ->
+ classDef.endsWith("MyAccount;") && method.name == "isPremiumSubscriber"
+ }
+}
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
new file mode 100644
index 000000000..8a3a0f810
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.reddit.layout.premiumicon
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockPremiumIconPatch = bytecodePatch(
+ name = "Unlock premium Reddit icons",
+ description = "Unlocks the premium Reddit icons.",
+) {
+ compatibleWith("com.reddit.frontpage")
+
+ val hasPremiumIconAccessMatch by hasPremiumIconAccessFingerprint()
+
+ execute {
+ hasPremiumIconAccessMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/extension/ExtensionPatch.kt
new file mode 100644
index 000000000..db449a93e
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/extension/ExtensionPatch.kt
@@ -0,0 +1,5 @@
+package app.revanced.patches.reddit.misc.extension
+
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch()
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
new file mode 100644
index 000000000..3381fd2bb
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.reddit.misc.tracking.url
+
+import app.revanced.patcher.fingerprint
+
+internal val shareLinkFormatterFingerprint = fingerprint {
+ custom { _, classDef ->
+ classDef.startsWith("Lcom/reddit/sharing/") && classDef.sourceFile == "UrlUtil.kt"
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..fb14ca80c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.reddit.misc.tracking.url
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val sanitizeUrlQueryPatch = bytecodePatch(
+ name = "Sanitize sharing links",
+ description = "Removes (tracking) query parameters from the URLs when sharing links.",
+) {
+ compatibleWith("com.reddit.frontpage")
+
+ val shareLinkFormatterMatch by shareLinkFormatterFingerprint()
+
+ execute {
+ shareLinkFormatterMatch.mutableMethod.addInstructions(
+ 0,
+ "return-object p0",
+ )
+ }
+}
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
new file mode 100644
index 000000000..f7efe3103
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt
@@ -0,0 +1,12 @@
+package app.revanced.patches.serviceportalbund.detection.root
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val rootDetectionFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("V")
+ custom { _, classDef ->
+ classDef.endsWith("/DeviceIntegrityCheck;")
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..40723ca27
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.serviceportalbund.detection.root
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val rootDetectionPatch = bytecodePatch(
+ name = "Remove root detection",
+ description = "Removes the check for root permissions and unlocked bootloader.",
+) {
+ compatibleWith("at.gv.bka.serviceportal")
+
+ val rootDetectionMatch by rootDetectionFingerprint()
+
+ execute {
+ rootDetectionMatch.mutableMethod.addInstruction(0, "return-void")
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt
new file mode 100644
index 000000000..df927dd4a
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.shared
+
+import app.revanced.patcher.fingerprint
+
+internal val castContextFetchFingerprint = fingerprint {
+ strings("Error fetching CastContext.")
+}
+
+internal val primeMethodFingerprint = fingerprint {
+ strings("com.google.android.GoogleCamera", "com.android.vending")
+}
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
new file mode 100644
index 000000000..8c3066c91
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt
@@ -0,0 +1,111 @@
+package app.revanced.patches.shared.misc.checks
+
+import android.os.Build.*
+import app.revanced.patcher.Fingerprint
+import app.revanced.patcher.Match
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+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
+import com.android.tools.smali.dexlib2.immutable.value.ImmutableStringEncodedValue
+import java.nio.charset.StandardCharsets
+import java.security.MessageDigest
+import kotlin.io.encoding.Base64
+import kotlin.io.encoding.ExperimentalEncodingApi
+
+private const val EXTENSION_CLASS_DESCRIPTOR =
+ "Lapp/revanced/extension/shared/checks/CheckEnvironmentPatch;"
+
+fun checkEnvironmentPatch(
+ mainActivityOnCreateFingerprint: Fingerprint,
+ extensionPatch: Patch<*>,
+ vararg compatiblePackages: String,
+) = bytecodePatch(
+ description = "Checks, if the application was patched by, otherwise warns the user.",
+) {
+ compatibleWith(*compatiblePackages)
+
+ dependsOn(
+ extensionPatch,
+ addResourcesPatch,
+ )
+
+ val patchInfoMatch by patchInfoFingerprint()
+ val patchInfoBuildMatch by patchInfoBuildFingerprint()
+ val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint()
+
+ execute {
+ addResources("shared", "misc.checks.checkEnvironmentPatch")
+
+ fun setPatchInfo() {
+ patchInfoMatch.setClassFields(
+ "PATCH_TIME" to System.currentTimeMillis().encoded,
+ )
+
+ fun setBuildInfo() {
+ patchInfoBuildMatch.setClassFields(
+ "PATCH_BOARD" to BOARD.encodedAndHashed,
+ "PATCH_BOOTLOADER" to BOOTLOADER.encodedAndHashed,
+ "PATCH_BRAND" to BRAND.encodedAndHashed,
+ "PATCH_CPU_ABI" to CPU_ABI.encodedAndHashed,
+ "PATCH_CPU_ABI2" to CPU_ABI2.encodedAndHashed,
+ "PATCH_DEVICE" to DEVICE.encodedAndHashed,
+ "PATCH_DISPLAY" to DISPLAY.encodedAndHashed,
+ "PATCH_FINGERPRINT" to FINGERPRINT.encodedAndHashed,
+ "PATCH_HARDWARE" to HARDWARE.encodedAndHashed,
+ "PATCH_HOST" to HOST.encodedAndHashed,
+ "PATCH_ID" to ID.encodedAndHashed,
+ "PATCH_MANUFACTURER" to MANUFACTURER.encodedAndHashed,
+ "PATCH_MODEL" to MODEL.encodedAndHashed,
+ "PATCH_PRODUCT" to PRODUCT.encodedAndHashed,
+ "PATCH_RADIO" to RADIO.encodedAndHashed,
+ "PATCH_TAGS" to TAGS.encodedAndHashed,
+ "PATCH_TYPE" to TYPE.encodedAndHashed,
+ "PATCH_USER" to USER.encodedAndHashed,
+ )
+ }
+
+ try {
+ Class.forName("android.os.Build")
+ // This only works on Android,
+ // because it uses Android APIs.
+ setBuildInfo()
+ } catch (_: ClassNotFoundException) {
+ }
+ }
+
+ fun invokeCheck() = mainActivityOnCreateMatch.mutableMethod?.addInstructions(
+ 0,
+ "invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
+ )
+
+ setPatchInfo()
+ invokeCheck()
+ }
+}
+
+@OptIn(ExperimentalEncodingApi::class)
+private val String.encodedAndHashed
+ get() = MutableStringEncodedValue(
+ ImmutableStringEncodedValue(
+ Base64.encode(
+ MessageDigest.getInstance("SHA-1")
+ .digest(this.toByteArray(StandardCharsets.UTF_8)),
+ ),
+ ),
+ )
+
+private val Long.encoded get() = MutableLongEncodedValue(ImmutableLongEncodedValue(this))
+
+private fun Match.setClassFields(vararg fieldNameValues: Pair) {
+ val fieldNameValueMap = mapOf(*fieldNameValues)
+
+ mutableClass.fields.forEach { field ->
+ field.initialValue = fieldNameValueMap[field.name] ?: return@forEach
+ }
+}
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
new file mode 100644
index 000000000..0eabd2f54
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.shared.misc.checks
+
+import app.revanced.patcher.fingerprint
+
+internal val patchInfoFingerprint = fingerprint {
+ custom { _, classDef -> classDef.type == "Lapp/revanced/extension/shared/checks/PatchInfo;" }
+}
+
+internal val patchInfoBuildFingerprint = fingerprint {
+ custom { _, classDef -> classDef.type == "Lapp/revanced/extension/shared/checks/PatchInfo\$Build;" }
+}
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
new file mode 100644
index 000000000..58cc5082f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt
@@ -0,0 +1,13 @@
+package app.revanced.patches.shared.misc.extension
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val revancedUtilsPatchesVersionFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Ljava/lang/String;")
+ parameters()
+ custom { method, _ ->
+ method.name == "getPatchesReleaseVersion" && method.definingClass == EXTENSION_CLASS_DESCRIPTOR
+ }
+}
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
new file mode 100644
index 000000000..3d7a1e444
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt
@@ -0,0 +1,100 @@
+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.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.fingerprint
+import app.revanced.patcher.patch.PatchException
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.util.exception
+import com.android.tools.smali.dexlib2.iface.Method
+import java.net.URLDecoder
+import java.util.jar.JarFile
+
+internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/Utils;"
+
+fun sharedExtensionPatch(
+ vararg hooks: ExtensionHook,
+) = bytecodePatch {
+ extendWith("extensions/shared.rve")
+
+ val revancedUtilsPatchesVersionMatch by revancedUtilsPatchesVersionFingerprint()
+ hooks.forEach { it.fingerprint() }
+
+ execute { context ->
+ if (context.classBy { EXTENSION_CLASS_DESCRIPTOR in it.type } == null) {
+ throw PatchException(
+ "Shared extension has not been merged yet. This patch can not succeed without merging it.",
+ )
+ }
+
+ hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) }
+
+ // Modify Utils method to include the patches release version.
+ revancedUtilsPatchesVersionMatch.mutableMethod.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()
+
+ if (urlString.startsWith("jar:file:")) {
+ val end = urlString.lastIndexOf('!')
+
+ 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")
+
+ addInstructions(
+ 0,
+ """
+ const-string v0, "$manifestValue"
+ return-object v0
+ """,
+ )
+ }
+ }
+}
+
+class ExtensionHook internal constructor(
+ val fingerprint: Fingerprint,
+ private val insertIndexResolver: ((Method) -> Int),
+ private val contextRegisterResolver: (Method) -> Int,
+) {
+ operator fun invoke(extensionClassDescriptor: String) {
+ fingerprint.match?.mutableMethod?.let { method ->
+ val insertIndex = insertIndexResolver(method)
+ val contextRegister = contextRegisterResolver(method)
+
+ method.addInstruction(
+ insertIndex,
+ "invoke-static/range { v$contextRegister .. v$contextRegister }, " +
+ "$extensionClassDescriptor->setContext(Landroid/content/Context;)V",
+ )
+ } ?: throw fingerprint.exception
+ }
+}
+
+fun extensionHook(
+ insertIndexResolver: ((Method) -> Int) = { 0 },
+ contextRegisterResolver: (Method) -> Int = { it.implementation!!.registerCount - 1 },
+ fingerprintBuilderBlock: FingerprintBuilder.() -> Unit,
+) = ExtensionHook(fingerprint(block = fingerprintBuilderBlock), insertIndexResolver, contextRegisterResolver)
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
new file mode 100644
index 000000000..80d041ada
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.shared.misc.fix.verticalscroll
+
+import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val canScrollVerticallyFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ parameters()
+ opcodes(
+ Opcode.MOVE_RESULT,
+ Opcode.RETURN,
+ Opcode.INVOKE_VIRTUAL,
+ 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
new file mode 100644
index 000000000..2ebb6a234
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt
@@ -0,0 +1,26 @@
+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.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val verticalScrollPatch = bytecodePatch(
+ description = "Fixes issues with refreshing the feed when the first component is of type EmptyComponent.",
+) {
+ val canScrollVerticallyMatch by canScrollVerticallyFingerprint()
+
+ execute {
+ canScrollVerticallyMatch.mutableMethod.apply {
+ val moveResultIndex = canScrollVerticallyMatch.patternMatch!!.endIndex
+ val moveResultRegister = getInstruction(moveResultIndex).registerA
+
+ 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
new file mode 100644
index 000000000..b5f613d54
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt
@@ -0,0 +1,30 @@
+package app.revanced.patches.shared.misc.gms
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId"
+
+internal val gmsCoreSupportFingerprint = fingerprint {
+ custom { _, classDef ->
+ classDef.endsWith("GmsCoreSupport;")
+ }
+}
+
+internal val googlePlayUtilityFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("I")
+ parameters("L", "I")
+ strings(
+ "This should never happen.",
+ "MetadataValueReader",
+ "com.google.android.gms",
+ )
+}
+
+internal val serviceCheckFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("V")
+ parameters("L", "I")
+ strings("Google Play Services not available")
+}
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
new file mode 100644
index 000000000..c338385bf
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt
@@ -0,0 +1,614 @@
+package app.revanced.patches.shared.misc.gms
+
+import app.revanced.patcher.Fingerprint
+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.patch.*
+import app.revanced.patches.all.misc.packagename.changePackageNamePatch
+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.gms.Constants.ACTIONS
+import app.revanced.patches.shared.misc.gms.Constants.AUTHORITIES
+import app.revanced.patches.shared.misc.gms.Constants.PERMISSIONS
+import app.revanced.util.exception
+import app.revanced.util.getReference
+import app.revanced.util.indexOfFirstInstruction
+import app.revanced.util.returnEarly
+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.MethodReference
+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 org.w3c.dom.Element
+import org.w3c.dom.Node
+
+private const val PACKAGE_NAME_REGEX_PATTERN = "^[a-z]\\w*(\\.[a-z]\\w*)+\$"
+
+/**
+ * A patch that allows patched Google apps to run without root and under a different package name
+ * by using GmsCore instead of Google Play Services.
+ *
+ * @param fromPackageName The package name of the original app.
+ * @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
+ * @param 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 mainActivityOnCreateFingerprint The fingerprint of the main activity onCreate method.
+ * @param extensionPatch The patch responsible for the extension.
+ * @param gmsCoreSupportResourcePatchFactory The factory for the corresponding resource patch
+ * that is used to patch the resources.
+ * @param executeBlock The additional execution block of the patch.
+ * @param block The additional block to build the patch.
+ */
+fun gmsCoreSupportPatch(
+ fromPackageName: String,
+ toPackageName: String,
+ primeMethodFingerprint: Fingerprint? = null,
+ earlyReturnFingerprints: Set = setOf(),
+ mainActivityOnCreateFingerprint: Fingerprint,
+ extensionPatch: Patch<*>,
+ gmsCoreSupportResourcePatchFactory: (gmsCoreVendorGroupIdOption: Option) -> Patch<*>,
+ executeBlock: Patch.(BytecodePatchContext) -> Unit = {},
+ block: BytecodePatchBuilder.() -> Unit = {},
+) = bytecodePatch(
+ name = "GmsCore support",
+ description = "Allows patched Google apps to run without root and under a different package name " +
+ "by using GmsCore instead of Google Play Services.",
+) {
+ val gmsCoreVendorGroupIdOption = stringOption(
+ key = "gmsCoreVendorGroupId",
+ default = "app.revanced",
+ values =
+ mapOf(
+ "ReVanced" to "app.revanced",
+ ),
+ title = "GmsCore vendor group ID",
+ description = "The vendor's group ID for GmsCore.",
+ required = true,
+ ) { it!!.matches(Regex(PACKAGE_NAME_REGEX_PATTERN)) }
+
+ dependsOn(
+ changePackageNamePatch,
+ gmsCoreSupportResourcePatchFactory(gmsCoreVendorGroupIdOption),
+ extensionPatch,
+ )
+
+ val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
+
+ val gmsCoreSupportMatch by gmsCoreSupportFingerprint()
+ val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint()
+ primeMethodFingerprint?.invoke()
+ googlePlayUtilityFingerprint()
+ serviceCheckFingerprint()
+ earlyReturnFingerprints.forEach { it() }
+
+ execute { context ->
+ fun transformStringReferences(transform: (str: String) -> String?) = context.classes.forEach {
+ val mutableClass by lazy {
+ context.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),
+ ),
+ )
+ }
+ }
+ }
+
+ // region Collection of transformations that are applied to all strings.
+
+ fun commonTransform(referencedString: String): String? =
+ when (referencedString) {
+ "com.google",
+ "com.google.android.gms",
+ in PERMISSIONS,
+ in ACTIONS,
+ in AUTHORITIES,
+ -> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
+
+ // No vendor prefix for whatever reason...
+ "subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
+ else -> null
+ }
+
+ fun contentUrisTransform(str: String): String? {
+ // only when content:// uri
+ if (str.startsWith("content://")) {
+ // check if matches any authority
+ for (authority in AUTHORITIES) {
+ val uriPrefix = "content://$authority"
+ if (str.startsWith(uriPrefix)) {
+ return str.replace(
+ uriPrefix,
+ "content://${authority.replace("com.google", gmsCoreVendorGroupId!!)}",
+ )
+ }
+ }
+
+ // gms also has a 'subscribedfeeds' authority, check for that one too
+ val subFeedsUriPrefix = "content://subscribedfeeds"
+ if (str.startsWith(subFeedsUriPrefix)) {
+ return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendorGroupId.subscribedfeeds")
+ }
+ }
+
+ return null
+ }
+
+ fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? = { string ->
+ when (string) {
+ "$fromPackageName.SuggestionsProvider",
+ "$fromPackageName.fileprovider",
+ -> string.replace(fromPackageName, toPackageName)
+
+ else -> null
+ }
+ }
+
+ fun transformPrimeMethod(packageName: String) {
+ primeMethodFingerprint!!.match?.mutableMethod?.apply {
+ var register = 2
+
+ val index = instructions.indexOfFirst {
+ if (it.getReference()?.string != fromPackageName) return@indexOfFirst false
+
+ register = (it as OneRegisterInstruction).registerA
+ return@indexOfFirst true
+ }
+
+ replaceInstruction(index, "const-string v$register, \"$packageName\"")
+ } ?: throw primeMethodFingerprint.exception
+ }
+
+ // endregion
+
+ val packageName = setOrGetFallbackPackageName(toPackageName)
+
+ // Transform all strings using all provided transforms, first match wins.
+ val transformations = arrayOf(
+ ::commonTransform,
+ ::contentUrisTransform,
+ packageNameTransform(fromPackageName, packageName),
+ )
+ transformStringReferences transform@{ string ->
+ transformations.forEach { transform ->
+ transform(string)?.let { transformedString -> return@transform transformedString }
+ }
+
+ return@transform null
+ }
+
+ // Specific method that needs to be patched.
+ primeMethodFingerprint?.let { transformPrimeMethod(packageName) }
+
+ // Return these methods early to prevent the app from crashing.
+ earlyReturnFingerprints.returnEarly()
+ serviceCheckFingerprint.returnEarly()
+
+ // Google Play Utility is not present in all apps, so we need to check if it's present.
+ if (googlePlayUtilityFingerprint.match != null) {
+ googlePlayUtilityFingerprint.returnEarly()
+ }
+
+ // Verify GmsCore is installed and whitelisted for power optimizations and background usage.
+ mainActivityOnCreateMatch.mutableMethod.apply {
+ // Temporary fix for patches with an extension patch that hook the onCreate method as well.
+ val setContextIndex = indexOfFirstInstruction {
+ val reference = getReference() ?: return@indexOfFirstInstruction false
+
+ reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V"
+ }
+
+ // Add after setContext call, because this patch needs the context.
+ addInstructions(
+ if (setContextIndex < 0) 0 else setContextIndex + 1,
+ "invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" +
+ "checkGmsCore(Landroid/app/Activity;)V",
+ )
+ }
+
+ // Change the vendor of GmsCore in the extension.
+ gmsCoreSupportMatch.mutableClass.methods
+ .single { it.name == GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME }
+ .replaceInstruction(0, "const-string v0, \"$gmsCoreVendorGroupId\"")
+
+ executeBlock(context)
+ }
+
+ block()
+}
+
+/**
+ * A collection of permissions, intents and content provider authorities
+ * that are present in GmsCore which need to be transformed.
+ */
+private object Constants {
+ /**
+ * All permissions.
+ */
+ val PERMISSIONS = setOf(
+ "com.google.android.c2dm.permission.RECEIVE",
+ "com.google.android.c2dm.permission.SEND",
+ "com.google.android.gms.auth.api.phone.permission.SEND",
+ "com.google.android.gms.permission.AD_ID",
+ "com.google.android.gms.permission.AD_ID_NOTIFICATION",
+ "com.google.android.gms.permission.CAR_FUEL",
+ "com.google.android.gms.permission.CAR_INFORMATION",
+ "com.google.android.gms.permission.CAR_MILEAGE",
+ "com.google.android.gms.permission.CAR_SPEED",
+ "com.google.android.gms.permission.CAR_VENDOR_EXTENSION",
+ "com.google.android.googleapps.permission.GOOGLE_AUTH",
+ "com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
+ "com.google.android.googleapps.permission.GOOGLE_AUTH.local",
+ "com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
+ "com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
+ "com.google.android.gtalkservice.permission.GTALK_SERVICE",
+ "com.google.android.providers.gsf.permission.READ_GSERVICES",
+ )
+
+ /**
+ * All intent actions.
+ */
+ val ACTIONS = setOf(
+ "com.google.android.c2dm.intent.RECEIVE",
+ "com.google.android.c2dm.intent.REGISTER",
+ "com.google.android.c2dm.intent.REGISTRATION",
+ "com.google.android.c2dm.intent.UNREGISTER",
+ "com.google.android.contextmanager.service.ContextManagerService.START",
+ "com.google.android.gcm.intent.SEND",
+ "com.google.android.gms.accounts.ACCOUNT_SERVICE",
+ "com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS",
+ "com.google.android.gms.accountsettings.action.BROWSE_SETTINGS",
+ "com.google.android.gms.accountsettings.action.VIEW_SETTINGS",
+ "com.google.android.gms.accountsettings.MY_ACCOUNT",
+ "com.google.android.gms.accountsettings.PRIVACY_SETTINGS",
+ "com.google.android.gms.accountsettings.SECURITY_SETTINGS",
+ "com.google.android.gms.ads.gservice.START",
+ "com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION",
+ "com.google.android.gms.ads.service.CACHE",
+ "com.google.android.gms.ads.service.CONSENT_LOOKUP",
+ "com.google.android.gms.ads.service.HTTP",
+ "com.google.android.gms.analytics.service.START",
+ "com.google.android.gms.app.settings.GoogleSettingsLink",
+ "com.google.android.gms.appstate.service.START",
+ "com.google.android.gms.appusage.service.START",
+ "com.google.android.gms.asterism.service.START",
+ "com.google.android.gms.audiomodem.service.AudioModemService.START",
+ "com.google.android.gms.audit.service.START",
+ "com.google.android.gms.auth.account.authapi.START",
+ "com.google.android.gms.auth.account.authenticator.auto.service.START",
+ "com.google.android.gms.auth.account.authenticator.chromeos.START",
+ "com.google.android.gms.auth.account.authenticator.tv.service.START",
+ "com.google.android.gms.auth.account.data.service.START",
+ "com.google.android.gms.auth.api.credentials.PICKER",
+ "com.google.android.gms.auth.api.credentials.service.START",
+ "com.google.android.gms.auth.api.identity.service.authorization.START",
+ "com.google.android.gms.auth.api.identity.service.credentialsaving.START",
+ "com.google.android.gms.auth.api.identity.service.signin.START",
+ "com.google.android.gms.auth.api.phone.service.InternalService.START",
+ "com.google.android.gms.auth.api.signin.service.START",
+ "com.google.android.gms.auth.be.appcert.AppCertService",
+ "com.google.android.gms.auth.blockstore.service.START",
+ "com.google.android.gms.auth.config.service.START",
+ "com.google.android.gms.auth.cryptauth.cryptauthservice.START",
+ "com.google.android.gms.auth.GOOGLE_SIGN_IN",
+ "com.google.android.gms.auth.login.LOGIN",
+ "com.google.android.gms.auth.proximity.devicesyncservice.START",
+ "com.google.android.gms.auth.proximity.securechannelservice.START",
+ "com.google.android.gms.auth.proximity.START",
+ "com.google.android.gms.auth.service.START",
+ "com.google.android.gms.backup.ACTION_BACKUP_SETTINGS",
+ "com.google.android.gms.backup.G1_BACKUP",
+ "com.google.android.gms.backup.G1_RESTORE",
+ "com.google.android.gms.backup.GMS_MODULE_RESTORE",
+ "com.google.android.gms.beacon.internal.IBleService.START",
+ "com.google.android.gms.car.service.START",
+ "com.google.android.gms.carrierauth.service.START",
+ "com.google.android.gms.cast.firstparty.START",
+ "com.google.android.gms.cast.remote_display.service.START",
+ "com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE",
+ "com.google.android.gms.cast_mirroring.service.START",
+ "com.google.android.gms.checkin.BIND_TO_SERVICE",
+ "com.google.android.gms.chromesync.service.START",
+ "com.google.android.gms.clearcut.service.START",
+ "com.google.android.gms.common.account.CHOOSE_ACCOUNT",
+ "com.google.android.gms.common.download.START",
+ "com.google.android.gms.common.service.START",
+ "com.google.android.gms.common.telemetry.service.START",
+ "com.google.android.gms.config.START",
+ "com.google.android.gms.constellation.service.START",
+ "com.google.android.gms.credential.manager.service.firstparty.START",
+ "com.google.android.gms.deviceconnection.service.START",
+ "com.google.android.gms.drive.ApiService.RESET_AFTER_BOOT",
+ "com.google.android.gms.drive.ApiService.START",
+ "com.google.android.gms.drive.ApiService.STOP",
+ "com.google.android.gms.droidguard.service.INIT",
+ "com.google.android.gms.droidguard.service.PING",
+ "com.google.android.gms.droidguard.service.START",
+ "com.google.android.gms.enterprise.loader.service.START",
+ "com.google.android.gms.facs.cache.service.START",
+ "com.google.android.gms.facs.internal.service.START",
+ "com.google.android.gms.feedback.internal.IFeedbackService",
+ "com.google.android.gms.fido.credentialstore.internal_service.START",
+ "com.google.android.gms.fido.fido2.privileged.START",
+ "com.google.android.gms.fido.fido2.regular.START",
+ "com.google.android.gms.fido.fido2.zeroparty.START",
+ "com.google.android.gms.fido.sourcedevice.service.START",
+ "com.google.android.gms.fido.targetdevice.internal_service.START",
+ "com.google.android.gms.fido.u2f.privileged.START",
+ "com.google.android.gms.fido.u2f.thirdparty.START",
+ "com.google.android.gms.fido.u2f.zeroparty.START",
+ "com.google.android.gms.fitness.BleApi",
+ "com.google.android.gms.fitness.ConfigApi",
+ "com.google.android.gms.fitness.GoalsApi",
+ "com.google.android.gms.fitness.GoogleFitnessService.START",
+ "com.google.android.gms.fitness.HistoryApi",
+ "com.google.android.gms.fitness.InternalApi",
+ "com.google.android.gms.fitness.RecordingApi",
+ "com.google.android.gms.fitness.SensorsApi",
+ "com.google.android.gms.fitness.SessionsApi",
+ "com.google.android.gms.fonts.service.START",
+ "com.google.android.gms.freighter.service.START",
+ "com.google.android.gms.games.internal.connect.service.START",
+ "com.google.android.gms.games.PLAY_GAMES_UPGRADE",
+ "com.google.android.gms.games.service.START",
+ "com.google.android.gms.gass.START",
+ "com.google.android.gms.gmscompliance.service.START",
+ "com.google.android.gms.googlehelp.HELP",
+ "com.google.android.gms.googlehelp.service.GoogleHelpService.START",
+ "com.google.android.gms.growth.service.START",
+ "com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START",
+ "com.google.android.gms.icing.INDEX_SERVICE",
+ "com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE",
+ "com.google.android.gms.identity.service.BIND",
+ "com.google.android.gms.inappreach.service.START",
+ "com.google.android.gms.instantapps.START",
+ "com.google.android.gms.kids.service.START",
+ "com.google.android.gms.languageprofile.service.START",
+ "com.google.android.gms.learning.internal.dynamitesupport.START",
+ "com.google.android.gms.learning.intservice.START",
+ "com.google.android.gms.learning.predictor.START",
+ "com.google.android.gms.learning.trainer.START",
+ "com.google.android.gms.learning.training.background.START",
+ "com.google.android.gms.location.places.GeoDataApi",
+ "com.google.android.gms.location.places.PlaceDetectionApi",
+ "com.google.android.gms.location.places.PlacesApi",
+ "com.google.android.gms.location.reporting.service.START",
+ "com.google.android.gms.location.settings.LOCATION_HISTORY",
+ "com.google.android.gms.location.settings.LOCATION_REPORTING_SETTINGS",
+ "com.google.android.gms.locationsharing.api.START",
+ "com.google.android.gms.locationsharingreporter.service.START",
+ "com.google.android.gms.lockbox.service.START",
+ "com.google.android.gms.matchstick.lighter.service.START",
+ "com.google.android.gms.mdm.services.DeviceManagerApiService.START",
+ "com.google.android.gms.mdm.services.START",
+ "com.google.android.gms.mdns.service.START",
+ "com.google.android.gms.measurement.START",
+ "com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START",
+ "com.google.android.gms.nearby.connection.service.START",
+ "com.google.android.gms.nearby.fastpair.START",
+ "com.google.android.gms.nearby.messages.service.NearbyMessagesService.START",
+ "com.google.android.gms.nearby.sharing.service.NearbySharingService.START",
+ "com.google.android.gms.nearby.sharing.START_SERVICE",
+ "com.google.android.gms.notifications.service.START",
+ "com.google.android.gms.ocr.service.internal.START",
+ "com.google.android.gms.ocr.service.START",
+ "com.google.android.gms.oss.licenses.service.START",
+ "com.google.android.gms.payse.service.BIND",
+ "com.google.android.gms.people.contactssync.service.START",
+ "com.google.android.gms.people.service.START",
+ "com.google.android.gms.phenotype.service.START",
+ "com.google.android.gms.photos.autobackup.service.START",
+ "com.google.android.gms.playlog.service.START",
+ "com.google.android.gms.plus.service.default.INTENT",
+ "com.google.android.gms.plus.service.image.INTENT",
+ "com.google.android.gms.plus.service.internal.START",
+ "com.google.android.gms.plus.service.START",
+ "com.google.android.gms.potokens.service.START",
+ "com.google.android.gms.pseudonymous.service.START",
+ "com.google.android.gms.rcs.START",
+ "com.google.android.gms.reminders.service.START",
+ "com.google.android.gms.romanesco.MODULE_BACKUP_AGENT",
+ "com.google.android.gms.romanesco.service.START",
+ "com.google.android.gms.safetynet.service.START",
+ "com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE",
+ "com.google.android.gms.search.service.SEARCH_AUTH_START",
+ "com.google.android.gms.semanticlocation.service.START_ODLH",
+ "com.google.android.gms.sesame.service.BIND",
+ "com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS",
+ "com.google.android.gms.setup.auth.SecondDeviceAuth.START",
+ "com.google.android.gms.signin.service.START",
+ "com.google.android.gms.smartdevice.d2d.SourceDeviceService.START",
+ "com.google.android.gms.smartdevice.d2d.TargetDeviceService.START",
+ "com.google.android.gms.smartdevice.directtransfer.SourceDirectTransferService.START",
+ "com.google.android.gms.smartdevice.directtransfer.TargetDirectTransferService.START",
+ "com.google.android.gms.smartdevice.postsetup.PostSetupService.START",
+ "com.google.android.gms.smartdevice.setup.accounts.AccountsService.START",
+ "com.google.android.gms.smartdevice.wifi.START_WIFI_HELPER_SERVICE",
+ "com.google.android.gms.social.location.activity.service.START",
+ "com.google.android.gms.speech.service.START",
+ "com.google.android.gms.statementservice.EXECUTE",
+ "com.google.android.gms.stats.ACTION_UPLOAD_DROPBOX_ENTRIES",
+ "com.google.android.gms.tapandpay.service.BIND",
+ "com.google.android.gms.telephonyspam.service.START",
+ "com.google.android.gms.testsupport.service.START",
+ "com.google.android.gms.thunderbird.service.START",
+ "com.google.android.gms.trustagent.BridgeApi.START",
+ "com.google.android.gms.trustagent.StateApi.START",
+ "com.google.android.gms.trustagent.trustlet.trustletmanagerservice.BIND",
+ "com.google.android.gms.trustlet.bluetooth.service.BIND",
+ "com.google.android.gms.trustlet.connectionlessble.service.BIND",
+ "com.google.android.gms.trustlet.face.service.BIND",
+ "com.google.android.gms.trustlet.nfc.service.BIND",
+ "com.google.android.gms.trustlet.onbody.service.BIND",
+ "com.google.android.gms.trustlet.place.service.BIND",
+ "com.google.android.gms.trustlet.voiceunlock.service.BIND",
+ "com.google.android.gms.udc.service.START",
+ "com.google.android.gms.update.START_API_SERVICE",
+ "com.google.android.gms.update.START_SERVICE",
+ "com.google.android.gms.update.START_SINGLE_USER_API_SERVICE",
+ "com.google.android.gms.update.START_TV_API_SERVICE",
+ "com.google.android.gms.usagereporting.service.START",
+ "com.google.android.gms.userlocation.service.START",
+ "com.google.android.gms.vehicle.cabin.service.START",
+ "com.google.android.gms.vehicle.climate.service.START",
+ "com.google.android.gms.vehicle.info.service.START",
+ "com.google.android.gms.wallet.service.BIND",
+ "com.google.android.gms.walletp2p.service.firstparty.BIND",
+ "com.google.android.gms.walletp2p.service.zeroparty.BIND",
+ "com.google.android.gms.wearable.BIND",
+ "com.google.android.gms.wearable.BIND_LISTENER",
+ "com.google.android.gms.wearable.DATA_CHANGED",
+ "com.google.android.gms.wearable.MESSAGE_RECEIVED",
+ "com.google.android.gms.wearable.NODE_CHANGED",
+ "com.google.android.gsf.action.GET_GLS",
+ "com.google.android.location.settings.LOCATION_REPORTING_SETTINGS",
+ "com.google.android.mdd.service.START",
+ "com.google.android.mdh.service.listener.START",
+ "com.google.android.mdh.service.START",
+ "com.google.android.mobstore.service.START",
+ "com.google.firebase.auth.api.gms.service.START",
+ "com.google.firebase.dynamiclinks.service.START",
+ "com.google.iid.TOKEN_REQUEST",
+ "com.google.android.gms.location.places.ui.PICK_PLACE",
+ )
+
+ /**
+ * All content provider authorities.
+ */
+ val AUTHORITIES = setOf(
+ "com.google.android.gms.auth.accounts",
+ "com.google.android.gms.chimera",
+ "com.google.android.gms.fonts",
+ "com.google.android.gms.phenotype",
+ "com.google.android.gsf.gservices",
+ "com.google.settings",
+ )
+}
+
+/**
+ * Abstract resource patch that allows Google apps to run without root and under a different package name
+ * by using GmsCore instead of Google Play Services.
+ *
+ * @param fromPackageName The package name of the original app.
+ * @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
+ * @param spoofedPackageSignature The signature of the package to spoof to.
+ * @param gmsCoreVendorGroupIdOption The option to get the vendor group ID of GmsCore.
+ * @param executeBlock The additional execution block of the patch.
+ * @param block The additional block to build the patch.
+ */
+fun gmsCoreSupportResourcePatch(
+ fromPackageName: String,
+ toPackageName: String,
+ spoofedPackageSignature: String,
+ gmsCoreVendorGroupIdOption: Option,
+ executeBlock: Patch.(ResourcePatchContext) -> Unit = {},
+ block: ResourcePatchBuilder.() -> Unit = {},
+) = resourcePatch {
+ dependsOn(
+ changePackageNamePatch,
+ addResourcesPatch,
+ )
+
+ val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
+
+ execute { context ->
+ addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
+
+ /**
+ * Add metadata to manifest to support spoofing the package name and signature of GmsCore.
+ */
+ fun addSpoofingMetadata() {
+ fun Node.adoptChild(
+ tagName: String,
+ block: Element.() -> Unit,
+ ) {
+ val child = ownerDocument.createElement(tagName)
+ child.block()
+ appendChild(child)
+ }
+
+ context.document["AndroidManifest.xml"].use { document ->
+ val applicationNode =
+ document
+ .getElementsByTagName("application")
+ .item(0)
+
+ // Spoof package name and signature.
+ applicationNode.adoptChild("meta-data") {
+ setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_NAME")
+ setAttribute("android:value", fromPackageName)
+ }
+
+ applicationNode.adoptChild("meta-data") {
+ setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_SIGNATURE")
+ setAttribute("android:value", spoofedPackageSignature)
+ }
+
+ // GmsCore presence detection in extension.
+ applicationNode.adoptChild("meta-data") {
+ // TODO: The name of this metadata should be dynamic.
+ setAttribute("android:name", "app.revanced.MICROG_PACKAGE_NAME")
+ setAttribute("android:value", "$gmsCoreVendorGroupId.android.gms")
+ }
+ }
+ }
+
+ /**
+ * Patch the manifest to support GmsCore.
+ */
+ fun patchManifest() {
+ val packageName = setOrGetFallbackPackageName(toPackageName)
+
+ val transformations = mapOf(
+ "package=\"$fromPackageName" to "package=\"$packageName",
+ "android:authorities=\"$fromPackageName" to "android:authorities=\"$packageName",
+ "$fromPackageName.permission.C2D_MESSAGE" to "$packageName.permission.C2D_MESSAGE",
+ "$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" to "$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
+ "com.google.android.c2dm" to "$gmsCoreVendorGroupId.android.c2dm",
+ "com.google.android.libraries.photos.api.mars" to "$gmsCoreVendorGroupId.android.apps.photos.api.mars",
+ "" to "",
+ )
+
+ val manifest = context["AndroidManifest.xml"]
+ manifest.writeText(
+ transformations.entries.fold(manifest.readText()) { acc, (from, to) ->
+ acc.replace(
+ from,
+ to,
+ )
+ },
+ )
+ }
+
+ patchManifest()
+ addSpoofingMetadata()
+
+ executeBlock(context)
+ }
+
+ block()
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatch.kt
new file mode 100644
index 000000000..e372d926c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatch.kt
@@ -0,0 +1,123 @@
+package app.revanced.patches.shared.misc.hex
+
+import app.revanced.patcher.patch.PatchException
+import app.revanced.patcher.patch.rawResourcePatch
+import kotlin.math.max
+
+// 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.
+fun hexPatch(replacementsSupplier: () -> Set) = rawResourcePatch {
+ execute { context ->
+ replacementsSupplier().groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) ->
+ val targetFile = try {
+ context[targetFilePath, true]
+ } catch (e: Exception) {
+ throw PatchException("Could not find target file: $targetFilePath")
+ }
+
+ // 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 { replacement ->
+ replacement.replacePattern(targetFileBytes)
+ }
+
+ targetFile.writeBytes(targetFileBytes)
+ }
+ }
+}
+
+/**
+ * Represents a pattern to search for and its replacement pattern.
+ *
+ * @property pattern The pattern to search for.
+ * @property replacementPattern The pattern to replace the [pattern] with.
+ * @property targetFilePath The path to the file to make the changes in relative to the APK root.
+ */
+class Replacement(
+ private val pattern: String,
+ replacementPattern: String,
+ internal val targetFilePath: String,
+) {
+ private val patternBytes = pattern.toByteArrayPattern()
+ private val replacementPattern = replacementPattern.toByteArrayPattern()
+
+ init {
+ if (this.patternBytes.size != this.replacementPattern.size) {
+ throw PatchException("Pattern and replacement pattern must have the same length: $pattern")
+ }
+ }
+
+ /**
+ * Replaces the [patternBytes] with the [replacementPattern] in the [targetFileBytes].
+ *
+ * @param targetFileBytes The bytes of the file to make the changes in.
+ */
+ fun replacePattern(targetFileBytes: ByteArray) {
+ val startIndex = indexOfPatternIn(targetFileBytes)
+
+ if (startIndex == -1) {
+ throw PatchException("Pattern not found in target file: $pattern")
+ }
+
+ replacementPattern.copyInto(targetFileBytes, startIndex)
+ }
+
+ // TODO: Allow searching in a file channel instead of a byte array to reduce memory usage.
+ /**
+ * Returns the index of the first occurrence of [patternBytes] in the haystack
+ * using the Boyer-Moore algorithm.
+ *
+ * @param haystack The array to search in.
+ *
+ * @return The index of the first occurrence of the [patternBytes] in the haystack or -1
+ * if the [patternBytes] is not found.
+ */
+ private fun indexOfPatternIn(haystack: ByteArray): Int {
+ val needle = patternBytes
+
+ val haystackLength = haystack.size - 1
+ val needleLength = needle.size - 1
+ val right = IntArray(256) { -1 }
+
+ for (i in 0 until needleLength) right[needle[i].toInt().and(0xFF)] = i
+
+ var skip: Int
+ for (i in 0..haystackLength - needleLength) {
+ skip = 0
+
+ for (j in needleLength - 1 downTo 0) {
+ if (needle[j] != haystack[i + j]) {
+ skip = max(1, j - right[haystack[i + j].toInt().and(0xFF)])
+
+ break
+ }
+ }
+
+ if (skip == 0) return i
+ }
+ return -1
+ }
+
+ companion object {
+ /**
+ * Convert a string representing a pattern of hexadecimal bytes to a byte array.
+ *
+ * @return The byte array representing the pattern.
+ * @throws PatchException If the pattern is invalid.
+ */
+ private fun String.toByteArrayPattern() = try {
+ split(" ").map { it.toInt(16).toByte() }.toByteArray()
+ } catch (e: NumberFormatException) {
+ throw PatchException(
+ "Could not parse pattern: $this. A pattern is a sequence of case insensitive strings " +
+ "representing hexadecimal bytes separated by spaces",
+ e,
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt
similarity index 54%
rename from src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt
index 2c165e1f1..ef198b491 100644
--- a/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,31 +1,33 @@
package app.revanced.patches.shared.misc.mapping
-import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
-import app.revanced.patcher.patch.ResourcePatch
+import app.revanced.patcher.patch.resourcePatch
import org.w3c.dom.Element
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
-object ResourceMappingPatch : ResourcePatch() {
- private val resourceMappings = Collections.synchronizedList(mutableListOf())
+// TODO: Probably renaming the patch/this is a good idea.
+lateinit var resourceMappings: List
+ private set
- private val THREAD_COUNT = Runtime.getRuntime().availableProcessors()
- private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT)
+val resourceMappingPatch = resourcePatch {
+ val threadCount = Runtime.getRuntime().availableProcessors()
+ val threadPoolExecutor = Executors.newFixedThreadPool(threadCount)
- override fun execute(context: ResourceContext) {
- // sSve the file in memory to concurrently read from it.
- val resourceXmlFile = context.get("res/values/public.xml").readBytes()
+ val resourceMappings = Collections.synchronizedList(mutableListOf())
- for (threadIndex in 0 until THREAD_COUNT) {
+ execute { context ->
+ // Save the file in memory to concurrently read from it.
+ val resourceXmlFile = context["res/values/public.xml"].readBytes()
+
+ for (threadIndex in 0 until threadCount) {
threadPoolExecutor.execute thread@{
- context.xmlEditor[resourceXmlFile.inputStream()].use { editor ->
- val document = editor.file
+ context.document[resourceXmlFile.inputStream()].use { document ->
val resources = document.documentElement.childNodes
val resourcesLength = resources.length
- val jobSize = resourcesLength / THREAD_COUNT
+ val jobSize = resourcesLength / threadCount
val batchStart = jobSize * threadIndex
val batchEnd = jobSize * (threadIndex + 1)
@@ -50,12 +52,13 @@ object ResourceMappingPatch : ResourcePatch() {
}
threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
+
+ app.revanced.patches.shared.misc.mapping.resourceMappings = resourceMappings
}
-
- operator fun 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: String, val name: String, val id: Long)
}
+
+operator fun List.get(type: String, name: String) = resourceMappings.firstOrNull {
+ it.type == type && it.name == name
+}?.id ?: throw PatchException("Could not find resource type: $type name: $name")
+
+data class ResourceElement internal constructor(val type: String, val name: String, val id: Long)
diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt
similarity index 57%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt
index 1570cc3dc..ea14b4a7b 100644
--- a/src/main/kotlin/app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt
@@ -1,9 +1,9 @@
package app.revanced.patches.shared.misc.settings
-import app.revanced.patcher.PatchClass
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patches.all.misc.resources.AddResourcesPatch
+import app.revanced.patcher.patch.resourcePatch
+import app.revanced.patches.all.misc.resources.addResource
+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.IntentPreference
import app.revanced.util.ResourceGroup
@@ -11,42 +11,35 @@ import app.revanced.util.copyResources
import app.revanced.util.getNode
import app.revanced.util.insertFirst
import org.w3c.dom.Node
-import java.io.Closeable
/**
* A resource patch that adds settings to a settings fragment.
*
* @param rootPreference A pair of an intent preference and the name of the fragment file to add it to.
* If null, no preference will be added.
- * @param dependencies Additional dependencies of this patch.
+ * @param preferences A set of preferences to add to the ReVanced fragment.
*/
-abstract class BaseSettingsResourcePatch(
- private val rootPreference: Pair? = null,
- dependencies: Set = emptySet(),
-) : ResourcePatch(
- dependencies = setOf(AddResourcesPatch::class) + dependencies,
-),
- MutableSet by mutableSetOf(),
- Closeable {
- private lateinit var context: ResourceContext
+fun settingsPatch(
+ rootPreference: Pair? = null,
+ preferences: Set,
+) = resourcePatch {
+ dependsOn(addResourcesPatch)
- override fun execute(context: ResourceContext) {
+ execute { context ->
context.copyResources(
"settings",
ResourceGroup("xml", "revanced_prefs.xml"),
)
- this.context = context
-
- AddResourcesPatch(BaseSettingsResourcePatch::class)
+ addResources("shared", "misc.settings.settingsResourcePatch")
}
- override fun close() {
+ finalize { context ->
fun Node.addPreference(preference: BasePreference, prepend: Boolean = false) {
preference.serialize(ownerDocument) { resource ->
// TODO: Currently, resources can only be added to "values", which may not be the correct place.
// It may be necessary to ask for the desired resourceValue in the future.
- AddResourcesPatch("values", resource)
+ addResource("values", resource)
}.let { preferenceNode ->
insertFirst(preferenceNode)
}
@@ -54,19 +47,15 @@ abstract class BaseSettingsResourcePatch(
// Add the root preference to an existing fragment if needed.
rootPreference?.let { (intentPreference, fragment) ->
- context.xmlEditor["res/xml/$fragment.xml"].use { editor ->
- val document = editor.file
-
+ context.document["res/xml/$fragment.xml"].use { document ->
document.getNode("PreferenceScreen").addPreference(intentPreference, true)
}
}
// Add all preferences to the ReVanced fragment.
- context.xmlEditor["res/xml/revanced_prefs.xml"].use { editor ->
- val document = editor.file
-
+ context.document["res/xml/revanced_prefs.xml"].use { document ->
val revancedPreferenceScreenNode = document.getNode("PreferenceScreen")
- forEach { revancedPreferenceScreenNode.addPreference(it) }
+ preferences.forEach { revancedPreferenceScreenNode.addPreference(it) }
}
}
}
diff --git a/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
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt
diff --git a/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
similarity index 92%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt
index 381a2cf3d..648f5eefd 100644
--- a/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
@@ -1,6 +1,6 @@
package app.revanced.patches.shared.misc.settings.preference
-import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
+import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import java.io.Closeable
abstract class BasePreferenceScreen(
@@ -18,7 +18,7 @@ abstract class BasePreferenceScreen(
/**
* Finalize and insert root preference into resource patch
*/
- abstract fun commit(screen: PreferenceScreen)
+ abstract fun commit(screen: PreferenceScreenPreference)
open inner class Screen(
key: String? = null,
@@ -29,13 +29,13 @@ abstract class BasePreferenceScreen(
private val sorting: Sorting = Sorting.BY_TITLE,
) : BasePreferenceCollection(key, titleKey, preferences) {
- override fun transform(): PreferenceScreen {
- return PreferenceScreen(
+ override fun transform(): PreferenceScreenPreference {
+ return PreferenceScreenPreference(
key,
titleKey,
summaryKey,
sorting,
- // Screens and preferences are sorted at runtime by integrations code,
+ // Screens and preferences are sorted at runtime by extension code,
// so title sorting uses the localized language in use.
preferences = preferences + categories.map { it.transform() },
)
diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt
diff --git a/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
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt
diff --git a/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
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt
diff --git a/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
similarity index 70%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt
index 787115dba..d4ecaae7e 100644
--- a/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
@@ -6,7 +6,7 @@ import org.w3c.dom.Document
/**
* A non-interactive preference.
*
- * Typically used to present static text, but also used for custom integration code that responds to taps.
+ * Typically used to present static text, but also used for custom extension code that responds to taps.
*
* @param key The preference key.
* @param summaryKey The preference summary key.
@@ -19,17 +19,8 @@ class NonInteractivePreference(
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
tag: String = "Preference",
- val selectable: Boolean = false
+ val selectable: Boolean = false,
) : BasePreference(key, titleKey, summaryKey, tag) {
-
- @Deprecated("Here only for binary compatibility, and should be removed after the next major version update.")
- constructor(
- key: String,
- summaryKey: String? = "${key}_summary",
- tag: String = "Preference",
- selectable: Boolean = false
- ) : this(key, "${key}_title", summaryKey, tag, selectable)
-
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("android:selectable", selectable.toString())
diff --git a/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
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt
diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreen.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt
similarity index 95%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreen.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt
index 049966a2c..2b21a8413 100644
--- a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreen.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt
@@ -15,7 +15,7 @@ import org.w3c.dom.Document
* @param preferences The preferences in this screen.
*/
@Suppress("MemberVisibilityCanBePrivate")
-open class PreferenceScreen(
+open class PreferenceScreenPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
@@ -26,7 +26,7 @@ open class PreferenceScreen(
// an extra bundle parameter can be added to the preferences XML declaration.
// This would require bundling and referencing an additional XML file
// or adding new attributes to the attrs.xml file.
- // Since the key value is not currently used by integrations,
+ // 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(if (sorting == Sorting.UNSORTED) key else (key + sorting.keySuffix), titleKey, summaryKey, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt
diff --git a/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
similarity index 100%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt
diff --git a/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
similarity index 90%
rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt
rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt
index d329a9177..81de61569 100644
--- a/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
@@ -17,7 +17,7 @@ class TextPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
- tag: String = "app.revanced.integrations.shared.settings.preference.ResettableEditTextPreference",
+ tag: String = "app.revanced.extension.shared.settings.preference.ResettableEditTextPreference",
val inputType: InputType = InputType.TEXT
) : BasePreference(key, titleKey, summaryKey, tag) {
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
new file mode 100644
index 000000000..0e81d7e59
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt
@@ -0,0 +1,15 @@
+package app.revanced.patches.solidexplorer2.functionality.filesize
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val onReadyFingerprint = fingerprint {
+ 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
new file mode 100644
index 000000000..39a8ae64f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt
@@ -0,0 +1,25 @@
+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.patch.bytecodePatch
+import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
+
+@Suppress("unused")
+val removeFileSizeLimitPatch = bytecodePatch(
+ name = "Remove file size limit",
+ description = "Allows opening files larger than 2 MB in the text editor.",
+) {
+ compatibleWith("pl.solidexplorer2")
+
+ val onReadyMatch by onReadyFingerprint()
+
+ execute {
+ onReadyMatch.mutableMethod.apply {
+ val cmpIndex = onReadyMatch.patternMatch!!.startIndex + 1
+ val cmpResultRegister = getInstruction(cmpIndex).registerA
+
+ replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0")
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt
similarity index 60%
rename from src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt
index c32a7b097..d93b31af4 100644
--- a/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt
@@ -1,25 +1,24 @@
package app.revanced.patches.songpal.badge
-import app.revanced.util.exception
-import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patches.songpal.badge.fingerprints.CreateTabsFingerprint
+import app.revanced.patcher.patch.bytecodePatch
-@Patch(
+internal const val ACTIVITY_TAB_DESCRIPTOR = "Ljp/co/sony/vim/framework/ui/yourheadphones/YhContract\$Tab;"
+
+@Suppress("unused")
+val badgeTabPatch = bytecodePatch(
name = "Remove badge tab",
description = "Removes the badge tab from the activity tab.",
- compatiblePackages = [CompatiblePackage("com.sony.songpal.mdr")]
-)
-object BadgeTabPatch : BytecodePatch(setOf(CreateTabsFingerprint)) {
- const val ACTIVITY_TAB_DESCRIPTOR = "Ljp/co/sony/vim/framework/ui/yourheadphones/YhContract\$Tab;"
- private val arrayTabs = listOf("Log", "HealthCare")
+) {
+ compatibleWith("com.sony.songpal.mdr")
- override fun execute(context: BytecodeContext) {
- CreateTabsFingerprint.result?.mutableMethod?.apply {
+ val createTabsMatch by createTabsFingerprint()
+
+ val arrayTabs = listOf("Log", "HealthCare")
+
+ execute {
+ createTabsMatch.mutableMethod.apply {
removeInstructions(0, 2)
val arrayRegister = 0
@@ -35,7 +34,7 @@ object BadgeTabPatch : BytecodePatch(setOf(CreateTabsFingerprint)) {
const/4 v$indexRegister, $index
sget-object v$arrayItemRegister, $ACTIVITY_TAB_DESCRIPTOR->$tab:$ACTIVITY_TAB_DESCRIPTOR
aput-object v$arrayItemRegister, v$arrayRegister, v$indexRegister
- """
+ """,
)
}
@@ -47,9 +46,8 @@ object BadgeTabPatch : BytecodePatch(setOf(CreateTabsFingerprint)) {
"""
const/4 v$arrayRegister, ${arrayTabs.size}
new-array v$arrayRegister, v$arrayRegister, [$ACTIVITY_TAB_DESCRIPTOR
- """
+ """,
)
-
- } ?: throw CreateTabsFingerprint.exception
+ }
}
}
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
new file mode 100644
index 000000000..5d7498c5f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt
@@ -0,0 +1,54 @@
+package app.revanced.patches.songpal.badge
+
+import app.revanced.patcher.fingerprint
+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 {
+ 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
+ } ?: false
+ }
+}
+
+// Located @ com.sony.songpal.mdr.vim.activity.MdrRemoteBaseActivity.e#run (9.5.0)
+internal val showNotificationFingerprint = fingerprint {
+ 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
+ } ?: false
+ }
+}
+
+internal val expectedReference = ImmutableMethodReference(
+ "Lcom/google/android/material/bottomnavigation/BottomNavigationView;",
+ "getOrCreateBadge", // Non-obfuscated placeholder method name.
+ listOf("I"),
+ "Lcom/google/android/material/badge/BadgeDrawable;",
+)
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
new file mode 100644
index 000000000..4f59c5f6a
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.songpal.badge
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val removeNotificationBadgePatch = bytecodePatch(
+ name = "Remove notification badge",
+ description = "Removes the red notification badge from the activity tab.",
+) {
+ compatibleWith("com.sony.songpal.mdr"("10.1.0"))
+
+ val showNotificationMatch by showNotificationFingerprint()
+
+ execute {
+ showNotificationMatch.mutableMethod.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
new file mode 100644
index 000000000..28780ea57
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt
@@ -0,0 +1,30 @@
+package app.revanced.patches.soundcloud.ad
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val interceptFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("L")
+ parameters("L")
+ opcodes(
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.INVOKE_INTERFACE,
+ Opcode.MOVE_RESULT_OBJECT
+ )
+ strings("SC-Mob-UserPlan", "Configuration")
+}
+
+internal val userConsumerPlanConstructorFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ parameters(
+ "Ljava/lang/String;",
+ "Z",
+ "Ljava/lang/String;",
+ "Ljava/util/List;",
+ "Ljava/lang/String;",
+ "Ljava/lang/String;",
+ )
+}
diff --git a/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt
similarity index 66%
rename from src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt
index ea94b3f2a..19d692863 100644
--- a/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt
@@ -1,33 +1,29 @@
package app.revanced.patches.soundcloud.ad
-import app.revanced.patcher.data.BytecodeContext
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.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
-import app.revanced.patches.soundcloud.ad.fingerprints.InterceptFingerprint
-import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint
-import app.revanced.patches.soundcloud.ad.fingerprints.UserConsumerPlanConstructorFingerprint
-import app.revanced.util.resultOrThrow
+import app.revanced.patches.soundcloud.shared.featureConstructorFingerprint
-@Patch(
- name = "Hide ads",
- compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
-)
@Suppress("unused")
-object HideAdsPatch : BytecodePatch(
- setOf(FeatureConstructorFingerprint, UserConsumerPlanConstructorFingerprint, InterceptFingerprint),
+val hideAdsPatch = bytecodePatch(
+ name = "Hide ads",
) {
- override fun execute(context: BytecodeContext) {
+ compatibleWith("com.soundcloud.android")
+
+ val featureConstructorMatch by featureConstructorFingerprint()
+ val userConsumerPlanConstructorMatch by userConsumerPlanConstructorFingerprint()
+ val interceptMatch by interceptFingerprint()
+
+ execute {
// 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.resultOrThrow().mutableMethod.apply {
+ featureConstructorMatch.mutableMethod.apply {
val afterCheckNotNullIndex = 2
addInstructionsWithLabels(
afterCheckNotNullIndex,
@@ -49,7 +45,7 @@ object 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.resultOrThrow().mutableMethod.addInstructions(
+ userConsumerPlanConstructorMatch.mutableMethod.addInstructions(
0,
"""
const-string p1, "high_tier"
@@ -61,12 +57,10 @@ object HideAdsPatch : BytecodePatch(
)
// Prevent verification of an HTTP header containing the user's current plan, which would contradict the previous patch.
- InterceptFingerprint.resultOrThrow().let { result ->
- val conditionIndex = result.scanResult.patternScanResult!!.endIndex + 1
- result.mutableMethod.addInstruction(
- conditionIndex,
- "return-object p1",
- )
- }
+ val conditionIndex = interceptMatch.patternMatch!!.endIndex + 1
+ interceptMatch.mutableMethod.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
new file mode 100644
index 000000000..e41333879
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.soundcloud.analytics
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+val disableTelemetryPatch = bytecodePatch(
+ name = "Disable telemetry",
+ description = "Disables SoundCloud's telemetry system.",
+) {
+ compatibleWith("com.soundcloud.android")
+
+ val createTrackingApiMatch by createTrackingApiFingerprint()
+
+ execute {
+ // Empty the "backend" argument to abort the initializer.
+ createTrackingApiMatch.mutableMethod.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
new file mode 100644
index 000000000..2954b4d99
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt
@@ -0,0 +1,13 @@
+package app.revanced.patches.soundcloud.analytics
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val createTrackingApiFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("L")
+ custom { methodDef, _ ->
+ methodDef.name == "create"
+ }
+ strings("backend", "boogaloo")
+}
diff --git a/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt
similarity index 69%
rename from src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt
index 08446f986..89a2ff5fc 100644
--- a/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt
@@ -1,41 +1,34 @@
package app.revanced.patches.soundcloud.offlinesync
-import app.revanced.patcher.data.BytecodeContext
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.getInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
-import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsHeaderVerificationFingerprint
-import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsURLBuilderFingerprint
-import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint
+import app.revanced.patches.soundcloud.shared.featureConstructorFingerprint
import app.revanced.util.getReference
-import app.revanced.util.resultOrThrow
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
-@Patch(
- name = "Enable offline sync",
- compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
-)
@Suppress("unused")
-object EnableOfflineSyncPatch : BytecodePatch(
- setOf(
- FeatureConstructorFingerprint, DownloadOperationsURLBuilderFingerprint,
- DownloadOperationsHeaderVerificationFingerprint
- ),
+val enableOfflineSync = bytecodePatch(
+ name = "Enable offline sync",
) {
- override fun execute(context: BytecodeContext) {
+ compatibleWith("com.soundcloud.android")
+
+ val featureConstructorMatch by featureConstructorFingerprint()
+ val downloadOperationsURLBuilderMatch by downloadOperationsURLBuilderFingerprint()
+ val downloadOperationsHeaderVerificationMatch by downloadOperationsHeaderVerificationFingerprint()
+
+ execute {
// 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.resultOrThrow().mutableMethod.apply {
+ featureConstructorMatch.mutableMethod.apply {
val afterCheckNotNullIndex = 2
addInstructionsWithLabels(
@@ -53,7 +46,7 @@ object EnableOfflineSyncPatch : BytecodePatch(
// Patch the URL builder to use the HTTPS_STREAM endpoint
// instead of the offline sync endpoint to downloading the track.
- DownloadOperationsURLBuilderFingerprint.resultOrThrow().mutableMethod.apply {
+ downloadOperationsURLBuilderMatch.mutableMethod.apply {
val getEndpointsEnumFieldIndex = 1
val getEndpointsEnumFieldInstruction = getInstruction(getEndpointsEnumFieldIndex)
@@ -62,16 +55,16 @@ object EnableOfflineSyncPatch : BytecodePatch(
replaceInstruction(
getEndpointsEnumFieldIndex,
- "sget-object v$targetRegister, $endpointsType->HTTPS_STREAM:$endpointsType"
+ "sget-object v$targetRegister, $endpointsType->HTTPS_STREAM:$endpointsType",
)
}
// 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.resultOrThrow().mutableMethod.apply {
+ downloadOperationsHeaderVerificationMatch.mutableMethod.apply {
// The first three null checks need to be patched.
- getInstructions().asSequence().filter {
+ instructions.asSequence().filter {
it.opcode == Opcode.IF_EQZ
}.take(3).toList().map { it.location.index }.asReversed().forEach { nullCheckIndex ->
val headerStringRegister = getInstruction(nullCheckIndex).registerA
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
new file mode 100644
index 000000000..688fe3604
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt
@@ -0,0 +1,29 @@
+package app.revanced.patches.soundcloud.offlinesync
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val downloadOperationsURLBuilderFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Ljava/lang/String")
+ parameters("L", "L")
+ opcodes(
+ Opcode.IGET_OBJECT,
+ Opcode.SGET_OBJECT,
+ Opcode.FILLED_NEW_ARRAY,
+ )
+}
+
+internal val downloadOperationsHeaderVerificationFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("V")
+ parameters("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
new file mode 100644
index 000000000..3a50ae407
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt
@@ -0,0 +1,16 @@
+package app.revanced.patches.soundcloud.shared
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val featureConstructorFingerprint = fingerprint {
+ returns("V")
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ parameters("Ljava/lang/String;", "Z", "Ljava/util/List;")
+ opcodes(
+ Opcode.SGET_OBJECT,
+ Opcode.CHECK_CAST,
+ Opcode.INVOKE_VIRTUAL
+ )
+}
diff --git a/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt
similarity index 75%
rename from src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt
index 9418c9f76..30d8a0bb4 100644
--- a/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,20 +1,19 @@
+@file:Suppress("NAME_SHADOWING")
+
package app.revanced.patches.spotify.layout.theme
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
+import app.revanced.patcher.patch.resourcePatch
+import app.revanced.patcher.patch.stringOption
import org.w3c.dom.Element
-@Patch(
+@Suppress("unused")
+val customThemePatch = resourcePatch(
name = "Custom theme",
description = "Applies a custom theme.",
- compatiblePackages = [CompatiblePackage("com.spotify.music")],
-)
-@Suppress("unused")
-object CustomThemePatch : ResourcePatch() {
- private var backgroundColor by stringPatchOption(
+) {
+ compatibleWith("com.spotify.music")
+
+ val backgroundColor by stringOption(
key = "backgroundColor",
default = "@android:color/black",
title = "Primary background color",
@@ -22,7 +21,7 @@ object CustomThemePatch : ResourcePatch() {
required = true,
)
- private var backgroundColorSecondary by stringPatchOption(
+ val backgroundColorSecondary by stringOption(
key = "backgroundColorSecondary",
default = "#ff282828",
title = "Secondary background color",
@@ -30,7 +29,7 @@ object CustomThemePatch : ResourcePatch() {
required = true,
)
- private var accentColor by stringPatchOption(
+ val accentColor by stringOption(
key = "accentColor",
default = "#ff1ed760",
title = "Accent color",
@@ -38,7 +37,7 @@ object CustomThemePatch : ResourcePatch() {
required = true,
)
- private var accentColorPressed by stringPatchOption(
+ val accentColorPressed by stringOption(
key = "accentColorPressed",
default = "#ff169c46",
title = "Pressed dark theme accent color",
@@ -48,15 +47,13 @@ object CustomThemePatch : ResourcePatch() {
required = true,
)
- override fun execute(context: ResourceContext) {
+ execute { context ->
val backgroundColor = backgroundColor!!
val backgroundColorSecondary = backgroundColorSecondary!!
val accentColor = accentColor!!
val accentColorPressed = accentColorPressed!!
- context.xmlEditor["res/values/colors.xml"].use { editor ->
- val document = editor.file
-
+ context.document["res/values/colors.xml"].use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
for (i in 0 until resourcesNode.childNodes.length) {
diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/Fingerprints.kt
new file mode 100644
index 000000000..365235bee
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/Fingerprints.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.spotify.lite.ondemand
+
+import com.android.tools.smali.dexlib2.Opcode
+import app.revanced.patcher.fingerprint
+
+internal val onDemandFingerprint = fingerprint(fuzzyPatternScanThreshold = 2) {
+ returns("L")
+ parameters()
+ opcodes(
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT,
+ Opcode.INVOKE_STATIC,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.IF_EQZ,
+ Opcode.SGET_OBJECT,
+ Opcode.GOTO,
+ Opcode.SGET_OBJECT,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.MOVE_RESULT,
+ Opcode.IPUT,
+ Opcode.RETURN_OBJECT,
+ )
+}
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
new file mode 100644
index 000000000..d8a8940d2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt
@@ -0,0 +1,22 @@
+package app.revanced.patches.spotify.lite.ondemand
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val onDemandPatch = bytecodePatch(
+ name = "Enable on demand",
+ description = "Enables listening to songs on-demand, allowing to play any song from playlists, albums or artists without limitations. This does not remove ads.",
+) {
+ compatibleWith("com.spotify.lite")
+
+ val onDemandMatch by onDemandFingerprint()
+
+ execute {
+ // Spoof a premium account
+ onDemandMatch.mutableMethod.addInstruction(
+ onDemandMatch.patternMatch!!.endIndex - 1,
+ "const/4 v0, 0x2",
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/Fingerprints.kt
new file mode 100644
index 000000000..761e32206
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.spotify.navbar
+
+import app.revanced.patcher.fingerprint
+import app.revanced.util.literal
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val addNavBarItemFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("V")
+ literal { showBottomNavigationItemsTextId }
+}
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
new file mode 100644
index 000000000..857842fc6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt
@@ -0,0 +1,52 @@
+package app.revanced.patches.spotify.navbar
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+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.resourceMappingPatch
+import app.revanced.patches.shared.misc.mapping.resourceMappings
+
+internal var showBottomNavigationItemsTextId = -1L
+ private set
+internal var premiumTabId = -1L
+ private set
+
+private val premiumNavbarTabResourcePatch = resourcePatch {
+ dependsOn(resourceMappingPatch)
+
+ execute {
+ premiumTabId = resourceMappings["id", "premium_tab"]
+
+ showBottomNavigationItemsTextId = resourceMappings[
+ "bool",
+ "show_bottom_navigation_items_text",
+ ]
+ }
+}
+
+@Suppress("unused")
+val premiumNavbarTabPatch = bytecodePatch(
+ name = "Premium navbar tab",
+ description = "Hides the premium tab from the navigation bar.",
+) {
+ dependsOn(premiumNavbarTabResourcePatch)
+
+ compatibleWith("com.spotify.music")
+
+ val addNavbarItemMatch by addNavBarItemFingerprint()
+
+ // If the navigation bar item is the premium tab, do not add it.
+ execute {
+ addNavbarItemMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const v1, $premiumTabId
+ if-ne p5, v1, :continue
+ return-void
+ :continue
+ nop
+ """,
+ )
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt
similarity index 59%
rename from src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt
index 6b493c4b1..d7e3a3088 100644
--- a/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt
@@ -1,19 +1,16 @@
package app.revanced.patches.stocard.layout
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.childElementsSequence
import app.revanced.util.getNode
-@Patch(
- name = "Hide offers tab",
- compatiblePackages = [CompatiblePackage("de.stocard.stocard")],
-)
@Suppress("unused")
-object HideOffersTabPatch : ResourcePatch() {
- override fun execute(context: ResourceContext) {
+val hideOffersTabPatch = resourcePatch(
+ name = "Hide offers tab",
+) {
+ compatibleWith("de.stocard.stocard")
+
+ execute { context ->
context.document["res/menu/bottom_navigation_menu.xml"].use { document ->
document.getNode("menu").apply {
removeChild(
diff --git a/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt
similarity index 58%
rename from src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt
index eecdeb38d..80894391e 100644
--- a/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt
@@ -1,18 +1,15 @@
package app.revanced.patches.stocard.layout
-import app.revanced.patcher.data.ResourceContext
-import app.revanced.patcher.patch.ResourcePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.getNode
-@Patch(
- name = "Hide story bubbles",
- compatiblePackages = [CompatiblePackage("de.stocard.stocard")],
-)
@Suppress("unused")
-object HideStoryBubblesPatch : ResourcePatch() {
- override fun execute(context: ResourceContext) {
+val hideStoryBubblesPatch = resourcePatch(
+ name = "Hide story bubbles",
+) {
+ compatibleWith("de.stocard.stocard")
+
+ execute { context ->
context.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/subscription/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt
new file mode 100644
index 000000000..0458f45d3
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.strava.subscription
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val getSubscribedFingerprint = fingerprint {
+ opcodes(Opcode.IGET_BOOLEAN)
+ custom { method, classDef ->
+ classDef.endsWith("/SubscriptionDetailResponse;") && method.name == "getSubscribed"
+ }
+}
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
new file mode 100644
index 000000000..0d96767a2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt
@@ -0,0 +1,20 @@
+package app.revanced.patches.strava.subscription
+import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockSubscriptionPatch = bytecodePatch(
+ name = "Unlock subscription features",
+ description = "Unlocks \"Routes\", \"Matched Runs\" and \"Segment Efforts\".",
+) {
+ compatibleWith("com.strava")
+
+ val getSubscribedMatch by getSubscribedFingerprint()
+
+ execute {
+ getSubscribedMatch.mutableMethod.replaceInstruction(
+ getSubscribedMatch.patternMatch!!.startIndex,
+ "const/4 v0, 0x1",
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt
new file mode 100644
index 000000000..c1af24825
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt
@@ -0,0 +1,69 @@
+package app.revanced.patches.strava.upselling
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
+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
+
+@Suppress("unused")
+val disableSubscriptionSuggestionsPatch = bytecodePatch(
+ name = "Disable subscription suggestions",
+) {
+ compatibleWith("com.strava"("320.12"))
+
+ val getModulesMatch by getModulesFingerprint()
+
+ execute {
+ val helperMethodName = "getModulesIfNotUpselling"
+ val pageSuffix = "_upsell"
+ val label = "original"
+
+ val className = getModulesMatch.classDef.type
+ val originalMethod = getModulesMatch.mutableMethod
+ val returnType = originalMethod.returnType
+
+ getModulesMatch.mutableClass.methods.add(
+ ImmutableMethod(
+ className,
+ helperMethodName,
+ emptyList(),
+ returnType,
+ AccessFlags.PRIVATE.value,
+ null,
+ null,
+ MutableMethodImplementation(3),
+ ).toMutable().apply {
+ addInstructions(
+ """
+ iget-object v0, p0, $className->page:Ljava/lang/String;
+ const-string v1, "$pageSuffix"
+ invoke-virtual {v0, v1}, Ljava/lang/String;->endsWith(Ljava/lang/String;)Z
+ move-result v0
+ if-eqz v0, :$label
+ invoke-static {}, Ljava/util/Collections;->emptyList()Ljava/util/List;
+ move-result-object v0
+ return-object v0
+ :$label
+ iget-object v0, p0, $className->modules:Ljava/util/List;
+ return-object v0
+ """,
+ )
+ },
+ )
+
+ val getModulesIndex = getModulesMatch.patternMatch!!.startIndex
+ with(originalMethod) {
+ removeInstruction(getModulesIndex)
+ addInstructions(
+ getModulesIndex,
+ """
+ invoke-direct {p0}, $className->$helperMethodName()$returnType
+ move-result-object v0
+ """,
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..1204a36f2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt
@@ -0,0 +1,11 @@
+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
new file mode 100644
index 000000000..0efa845fb
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.swissid.integritycheck
+
+import app.revanced.patcher.fingerprint
+
+internal val checkIntegrityFingerprint = fingerprint {
+ returns("V")
+ parameters("Lcom/swisssign/deviceintegrity/model/DeviceIntegrityResult;")
+ strings("it", "result")
+}
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
new file mode 100644
index 000000000..82714c651
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt
@@ -0,0 +1,32 @@
+package app.revanced.patches.swissid.integritycheck
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+private const val RESULT_METHOD_REFERENCE = " Lcom/swisssign/deviceintegrity/DeviceintegrityPlugin\$onMethodCall\$1;->" +
+ "\$result:Lio/flutter/plugin/common/MethodChannel\$Result;"
+private const val SUCCESS_METHOD_REFERENCE =
+ "Lio/flutter/plugin/common/MethodChannel\$Result;->success(Ljava/lang/Object;)V"
+
+@Suppress("unused")
+val removeGooglePlayIntegrityCheckPatch = bytecodePatch(
+ name = "Remove Google Play Integrity check",
+ description = "Removes the Google Play Integrity check. With this it's possible to use SwissID on custom ROMS." +
+ "If the device is rooted, root permissions must be hidden from the app.",
+) {
+ compatibleWith("com.swisssign.swissid.mobile")
+
+ val checkIntegrityMatch by checkIntegrityFingerprint()
+
+ execute {
+ checkIntegrityMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ iget-object p1, p0, $RESULT_METHOD_REFERENCE
+ const-string v0, "VALID"
+ invoke-interface {p1, v0}, $SUCCESS_METHOD_REFERENCE
+ return-void
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..4bd688de4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt
@@ -0,0 +1,15 @@
+package app.revanced.patches.ticktick.misc.themeunlock
+
+import app.revanced.patcher.fingerprint
+
+internal val checkLockedThemesFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("Theme;") && method.name == "isLockedTheme"
+ }
+}
+
+internal val setThemeFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("ThemePreviewActivity;") && method.name == "lambda\$updateUserBtn\$1"
+ }
+}
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
new file mode 100644
index 000000000..5e2aa37d8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt
@@ -0,0 +1,28 @@
+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.patch.bytecodePatch
+
+@Suppress("unused")
+val unlockProPatch = bytecodePatch(
+ name = "Unlock themes",
+ description = "Unlocks all themes that are inaccessible until a certain level is reached.",
+) {
+ compatibleWith("com.ticktick.task")
+
+ val checkLockedThemesMatch by checkLockedThemesFingerprint()
+ val setThemeMatch by setThemeFingerprint()
+
+ execute {
+ checkLockedThemesMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x0
+ return v0
+ """,
+ )
+
+ setThemeMatch.mutableMethod.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
new file mode 100644
index 000000000..7301c58ba
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt
@@ -0,0 +1,48 @@
+package app.revanced.patches.tiktok.feedfilter
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
+import app.revanced.patcher.extensions.InstructionExtensions.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 com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
+
+@Suppress("unused")
+val feedFilterPatch = bytecodePatch(
+ name = "Feed filter",
+ description = "Removes ads, livestreams, stories, image videos " +
+ "and videos with a specific amount of views or likes from the feed.",
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ settingsPatch,
+ )
+
+ compatibleWith(
+ "com.ss.android.ugc.trill"("36.5.4"),
+ "com.zhiliaoapp.musically"("36.5.4"),
+ )
+
+ val feedApiServiceLIZMatch by feedApiServiceLIZFingerprint()
+ val settingsStatusLoadMatch by settingsStatusLoadFingerprint()
+
+ execute {
+ feedApiServiceLIZMatch.mutableMethod.apply {
+ val returnFeedItemInstruction = instructions.first { it.opcode == Opcode.RETURN_OBJECT }
+ val feedItemsRegister = (returnFeedItemInstruction as OneRegisterInstruction).registerA
+
+ addInstruction(
+ returnFeedItemInstruction.location.index,
+ "invoke-static { v$feedItemsRegister }, " +
+ "Lapp/revanced/extension/tiktok/feedfilter/FeedItemsFilter;->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
+ )
+ }
+
+ settingsStatusLoadMatch.mutableMethod.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
new file mode 100644
index 000000000..4f899661e
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.tiktok.feedfilter
+
+import app.revanced.patcher.fingerprint
+
+internal val feedApiServiceLIZFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList"
+ }
+}
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
new file mode 100644
index 000000000..eb2868374
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt
@@ -0,0 +1,10 @@
+package app.revanced.patches.tiktok.interaction.cleardisplay
+
+import app.revanced.patcher.fingerprint
+
+internal val onClearDisplayEventFingerprint = fingerprint {
+ custom { method, classDef ->
+ // Internally the feature is called "Clear mode".
+ classDef.endsWith("/ClearModePanelComponent;") && method.name == "onClearModeEvent"
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt
similarity index 61%
rename from src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt
index b1522e880..73374b513 100644
--- a/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,37 +1,30 @@
package app.revanced.patches.tiktok.interaction.cleardisplay
-import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
-import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnClearDisplayEventFingerprint
-import app.revanced.patches.tiktok.shared.fingerprints.OnRenderFirstFrameFingerprint
-import app.revanced.util.exception
+import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
-@Patch(
+@Suppress("unused")
+val rememberClearDisplayPatch = bytecodePatch(
name = "Remember clear display",
description = "Remembers the clear display configurations in between videos.",
- compatiblePackages = [
- CompatiblePackage("com.ss.android.ugc.trill", ["36.5.4"]),
- CompatiblePackage("com.zhiliaoapp.musically", ["36.5.4"]),
- ],
-)
-@Suppress("unused")
-object RememberClearDisplayPatch : BytecodePatch(
- setOf(
- OnClearDisplayEventFingerprint,
- OnRenderFirstFrameFingerprint,
- ),
) {
- override fun execute(context: BytecodeContext) {
- OnClearDisplayEventFingerprint.result?.mutableMethod?.let {
+ compatibleWith(
+ "com.ss.android.ugc.trill"("36.5.4"),
+ "com.zhiliaoapp.musically"("36.5.4"),
+ )
+
+ val onClearDisplayEventMatch by onClearDisplayEventFingerprint()
+ val onRenderFirstFrameMatch by onRenderFirstFrameFingerprint()
+
+ execute {
+ onClearDisplayEventMatch.mutableMethod.let {
// region Hook the "Clear display" configuration save event to remember the state of clear display.
val isEnabledIndex = it.indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1
@@ -40,7 +33,7 @@ object RememberClearDisplayPatch : BytecodePatch(
it.addInstructions(
isEnabledIndex,
"invoke-static { v$isEnabledRegister }, " +
- "Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V",
+ "Lapp/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V",
)
// endregion
@@ -48,10 +41,9 @@ object RememberClearDisplayPatch : BytecodePatch(
// region Override the "Clear display" configuration load event to load the state of clear display.
val clearDisplayEventClass = it.parameters[0].type
- OnRenderFirstFrameFingerprint.result?.mutableMethod?.apply {
- addInstructionsWithLabels(
- 0,
- """
+ onRenderFirstFrameMatch.mutableMethod.addInstructionsWithLabels(
+ 0,
+ """
# Create a new clearDisplayEvent and post it to the EventBus (https://github.com/greenrobot/EventBus)
# Clear display type such as 0 = LONG_PRESS, 1 = SCREEN_RECORD etc.
@@ -64,7 +56,7 @@ object RememberClearDisplayPatch : BytecodePatch(
const-string v3, "long_press"
# The state of clear display.
- invoke-static { }, Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->getClearDisplayState()Z
+ invoke-static { }, Lapp/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch;->getClearDisplayState()Z
move-result v4
if-eqz v4, :clear_display_disabled
@@ -72,11 +64,10 @@ object 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", getInstruction(0)),
- )
- } ?: throw OnRenderFirstFrameFingerprint.exception
+ ExternalLabel("clear_display_disabled", onRenderFirstFrameMatch.mutableMethod.getInstruction(0)),
+ )
// endregion
- } ?: throw OnClearDisplayEventFingerprint.exception
+ }
}
}
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
new file mode 100644
index 000000000..94afc211f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt
@@ -0,0 +1,98 @@
+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.replaceInstructions
+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.getReference
+import app.revanced.util.indexOfFirstInstructionOrThrow
+import com.android.tools.smali.dexlib2.iface.reference.MethodReference
+
+@Suppress("unused")
+val downloadsPatch = bytecodePatch(
+ name = "Downloads",
+ description = "Removes download restrictions and changes the default path to download to.",
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ settingsPatch,
+ )
+
+ compatibleWith(
+ "com.ss.android.ugc.trill"("36.5.4"),
+ "com.zhiliaoapp.musically"("36.5.4"),
+ )
+
+ val aclCommonShareMatch by aclCommonShareFingerprint()
+ val aclCommonShare2Match by aclCommonShare2Fingerprint()
+ val aclCommonShare3Match by aclCommonShare3Fingerprint()
+ val downloadUriMatch by downloadUriFingerprint()
+ val settingsStatusLoadMatch by settingsStatusLoadFingerprint()
+
+ execute { context ->
+ aclCommonShareMatch.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const/4 v0, 0x0
+ return v0
+ """,
+ )
+
+ aclCommonShare2Match.mutableMethod.replaceInstructions(
+ 0,
+ """
+ const/4 v0, 0x2
+ return v0
+ """,
+ )
+
+ // Download videos without watermark.
+ aclCommonShare3Match.mutableMethod.addInstructionsWithLabels(
+ 0,
+ """
+ invoke-static {}, Lapp/revanced/extension/tiktok/download/DownloadsPatch;->shouldRemoveWatermark()Z
+ move-result v0
+ if-eqz v0, :noremovewatermark
+ const/4 v0, 0x1
+ return v0
+ :noremovewatermark
+ nop
+ """,
+ )
+
+ // Change the download path patch.
+ downloadUriMatch.mutableMethod.apply {
+ val firstIndex = indexOfFirstInstructionOrThrow {
+ getReference()?.name == ""
+ }
+ val secondIndex = indexOfFirstInstructionOrThrow {
+ getReference()?.returnType?.contains("Uri") == true
+ }
+
+ addInstructions(
+ secondIndex,
+ """
+ invoke-static {}, Lapp/revanced/extension/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String;
+ move-result-object v0
+ """,
+ )
+
+ addInstructions(
+ firstIndex,
+ """
+ invoke-static {}, Lapp/revanced/extension/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String;
+ move-result-object v0
+ """,
+ )
+ }
+
+ settingsStatusLoadMatch.mutableMethod.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
new file mode 100644
index 000000000..f58141fac
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt
@@ -0,0 +1,47 @@
+package app.revanced.patches.tiktok.interaction.downloads
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val aclCommonShareFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("I")
+ custom { method, classDef ->
+ classDef.endsWith("/ACLCommonShare;") &&
+ method.name == "getCode"
+ }
+}
+
+internal val aclCommonShare2Fingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("I")
+ custom { method, classDef ->
+ classDef.endsWith("/ACLCommonShare;") &&
+ method.name == "getShowType"
+ }
+}
+
+internal val aclCommonShare3Fingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("I")
+ custom { method, classDef ->
+ classDef.endsWith("/ACLCommonShare;") &&
+ method.name == "getTranscode"
+ }
+}
+
+internal val downloadUriFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("Landroid/net/Uri;")
+ parameters(
+ "Landroid/content/Context;",
+ "Ljava/lang/String;"
+ )
+ strings(
+ "/",
+ "/Camera",
+ "/Camera/",
+ "video/mp4"
+ )
+}
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
new file mode 100644
index 000000000..ce372ea42
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.tiktok.interaction.seekbar
+
+import app.revanced.patcher.fingerprint
+
+internal val setSeekBarShowTypeFingerprint = fingerprint {
+ strings("seekbar show type change, change to:")
+}
+
+internal val shouldShowSeekBarFingerprint = fingerprint {
+ strings("can not show seekbar, state: 1, not in resume")
+}
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
new file mode 100644
index 000000000..5cb6db64c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt
@@ -0,0 +1,38 @@
+package app.revanced.patches.tiktok.interaction.seekbar
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val showSeekbarPatch = bytecodePatch(
+ name = "Show seekbar",
+ description = "Shows progress bar for all video.",
+) {
+ compatibleWith(
+ "com.ss.android.ugc.trill",
+ "com.zhiliaoapp.musically",
+ )
+
+ val shouldShowSeekBarMatch by shouldShowSeekBarFingerprint()
+ val setSeekBarShowTypeMatch by setSeekBarShowTypeFingerprint()
+
+ execute {
+ shouldShowSeekBarMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ setSeekBarShowTypeMatch.mutableMethod.apply {
+ val typeRegister = implementation!!.registerCount - 1
+
+ addInstructions(
+ 0,
+ """
+ const/16 v$typeRegister, 0x64
+ """,
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..221036bb9
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.tiktok.interaction.speed
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val getSpeedFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/BaseListFragmentPanel;") && method.name == "onFeedSpeedSelectedEvent"
+ }
+}
+
+internal val setSpeedFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
+ returns("V")
+ parameters("Ljava/lang/String;", "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", "F")
+ strings("enterFrom")
+}
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
new file mode 100644
index 000000000..81153a1dc
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt
@@ -0,0 +1,74 @@
+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.patch.bytecodePatch
+import app.revanced.patches.tiktok.shared.getEnterFromFingerprint
+import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint
+import app.revanced.util.getReference
+import app.revanced.util.indexOfFirstInstructionOrThrow
+import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x
+import com.android.tools.smali.dexlib2.iface.reference.MethodReference
+
+@Suppress("unused")
+val playbackSpeedPatch = bytecodePatch(
+ name = "Playback speed",
+ description = "Enables the playback speed option for all videos and " +
+ "retains the speed configurations in between videos.",
+) {
+ compatibleWith(
+ "com.ss.android.ugc.trill"("36.5.4"),
+ "com.zhiliaoapp.musically"("36.5.4"),
+ )
+
+ val getSpeedMatch by getSpeedFingerprint()
+ val onRenderFirstFrameMatch by onRenderFirstFrameFingerprint()
+ val setSpeedMatch by setSpeedFingerprint()
+ val getEnterFromMatch by getEnterFromFingerprint()
+
+ execute {
+ setSpeedMatch.let { onVideoSwiped ->
+ getSpeedMatch.mutableMethod.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.
+ onRenderFirstFrameMatch.mutableMethod.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 }, ${getEnterFromMatch.method}
+ 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.method}
+ """,
+ )
+
+ // Force enable the playback speed option for all videos.
+ onVideoSwiped.mutableClass.methods.find { method -> method.returnType == "Z" }?.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ return v0
+ """,
+ )
+ }
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/ExtensionPatch.kt
new file mode 100644
index 000000000..b714b9d1c
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/ExtensionPatch.kt
@@ -0,0 +1,5 @@
+package app.revanced.patches.tiktok.misc.extension
+
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch(initHook)
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
new file mode 100644
index 000000000..24a49dd35
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt
@@ -0,0 +1,14 @@
+package app.revanced.patches.tiktok.misc.extension
+
+import app.revanced.patches.shared.misc.extension.extensionHook
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val initHook = extensionHook(
+ insertIndexResolver = { 1 }, // Insert after call to super class.
+) {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ custom { method, classDef ->
+ classDef.endsWith("/AwemeHostApplication;") &&
+ method.name == ""
+ }
+}
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
new file mode 100644
index 000000000..1b2f0a0b0
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt
@@ -0,0 +1,32 @@
+package app.revanced.patches.tiktok.misc.login.disablerequirement
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableLoginRequirementPatch = bytecodePatch(
+ name = "Disable login requirement",
+) {
+ compatibleWith(
+ "com.ss.android.ugc.trill",
+ "com.zhiliaoapp.musically",
+ )
+
+ val mandatoryLoginServiceMatch by mandatoryLoginServiceFingerprint()
+ val mandatoryLoginService2Match by mandatoryLoginService2Fingerprint()
+
+ execute {
+ listOf(
+ mandatoryLoginServiceMatch.mutableMethod,
+ mandatoryLoginService2Match.mutableMethod,
+ ).forEach { method ->
+ method.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x0
+ return v0
+ """,
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..929ef8672
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt
@@ -0,0 +1,17 @@
+package app.revanced.patches.tiktok.misc.login.disablerequirement
+
+import app.revanced.patcher.fingerprint
+
+internal val mandatoryLoginServiceFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/MandatoryLoginService;") &&
+ method.name == "enableForcedLogin"
+ }
+}
+
+internal val mandatoryLoginService2Fingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/MandatoryLoginService;") &&
+ method.name == "shouldShowForcedLogin"
+ }
+}
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
new file mode 100644
index 000000000..a40f5251f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt
@@ -0,0 +1,22 @@
+package app.revanced.patches.tiktok.misc.login.fixgoogle
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val googleAuthAvailableFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ parameters()
+ custom { method, _ ->
+ method.definingClass == "Lcom/bytedance/lobby/google/GoogleAuth;"
+ }
+}
+
+internal val googleOneTapAuthAvailableFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ parameters()
+ custom { method, _ ->
+ method.definingClass == "Lcom/bytedance/lobby/google/GoogleOneTapAuth;"
+ }
+}
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
new file mode 100644
index 000000000..214868d04
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt
@@ -0,0 +1,33 @@
+package app.revanced.patches.tiktok.misc.login.fixgoogle
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val fixGoogleLoginPatch = bytecodePatch(
+ name = "Fix Google login",
+ description = "Allows logging in with a Google account.",
+) {
+ compatibleWith(
+ "com.ss.android.ugc.trill",
+ "com.zhiliaoapp.musically",
+ )
+
+ val googleOneTapAuthAvailableMatch by googleOneTapAuthAvailableFingerprint()
+ val googleAuthAvailableMatch by googleAuthAvailableFingerprint()
+
+ execute {
+ listOf(
+ googleOneTapAuthAvailableMatch.mutableMethod,
+ googleAuthAvailableMatch.mutableMethod,
+ ).forEach { method ->
+ method.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x0
+ return v0
+ """,
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..d1c4d6de6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt
@@ -0,0 +1,35 @@
+package app.revanced.patches.tiktok.misc.settings
+
+import app.revanced.patcher.fingerprint
+
+internal val addSettingsEntryFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/SettingNewVersionFragment;") &&
+ method.name == "initUnitManger"
+ }
+}
+
+internal val adPersonalizationActivityOnCreateFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/AdPersonalizationActivity;") &&
+ method.name == "onCreate"
+ }
+}
+
+internal val settingsEntryFingerprint = fingerprint {
+ strings("pls pass item or extends the EventUnit")
+}
+
+internal val settingsEntryInfoFingerprint = fingerprint {
+ strings(
+ "ExposeItem(title=",
+ ", icon=",
+ )
+}
+
+internal val settingsStatusLoadFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("Lapp/revanced/extension/tiktok/settings/SettingsStatus;") &&
+ method.name == "load"
+ }
+}
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
new file mode 100644
index 000000000..79ee38711
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt
@@ -0,0 +1,102 @@
+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.patch.bytecodePatch
+import app.revanced.patcher.util.smali.ExternalLabel
+import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
+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
+
+internal const val EXTENSION_CLASS_DESCRIPTOR =
+ "Lapp/revanced/extension/tiktok/settings/AdPersonalizationActivityHook;"
+
+@Suppress("unused")
+val settingsPatch = bytecodePatch(
+ name = "Settings",
+ description = "Adds ReVanced settings to TikTok.",
+) {
+ dependsOn(sharedExtensionPatch)
+
+ compatibleWith(
+ "com.ss.android.ugc.trill"("36.5.4"),
+ "com.zhiliaoapp.musically"("36.5.4"),
+ )
+
+ val adPersonalizationActivityOnCreateMatch by adPersonalizationActivityOnCreateFingerprint()
+ val addSettingsEntryMatch by addSettingsEntryFingerprint()
+ val settingsEntryMatch by settingsEntryFingerprint()
+ val settingsEntryInfoMatch by settingsEntryInfoFingerprint()
+
+ execute {
+ val initializeSettingsMethodDescriptor =
+ "$EXTENSION_CLASS_DESCRIPTOR->initialize(" +
+ "Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;" +
+ ")Z"
+
+ val createSettingsEntryMethodDescriptor =
+ "$EXTENSION_CLASS_DESCRIPTOR->createSettingsEntry(" +
+ "Ljava/lang/String;" +
+ "Ljava/lang/String;" +
+ ")Ljava/lang/Object;"
+
+ fun String.toClassName(): String = substring(1, this.length - 1).replace("/", ".")
+
+ // Find the class name of classes which construct a settings entry
+ val settingsButtonClass = settingsEntryMatch.classDef.type.toClassName()
+ val settingsButtonInfoClass = settingsEntryInfoMatch.classDef.type.toClassName()
+
+ // Create a settings entry for 'revanced settings' and add it to settings fragment
+ addSettingsEntryMatch.mutableMethod.apply {
+ val markIndex = implementation!!.instructions.indexOfFirst {
+ it.opcode == Opcode.IGET_OBJECT && ((it as Instruction22c).reference as FieldReference).name == "headerUnit"
+ }
+
+ val getUnitManager = getInstruction(markIndex + 2)
+ val addEntry = getInstruction(markIndex + 1)
+
+ addInstructions(
+ markIndex + 2,
+ listOf(
+ getUnitManager,
+ addEntry,
+ ),
+ )
+
+ addInstructions(
+ markIndex + 2,
+ """
+ const-string v0, "$settingsButtonClass"
+ const-string v1, "$settingsButtonInfoClass"
+ invoke-static {v0, v1}, $createSettingsEntryMethodDescriptor
+ move-result-object v0
+ check-cast v0, ${settingsEntryMatch.classDef.type}
+ """,
+ )
+ }
+
+ // Initialize the settings menu once the replaced setting entry is clicked.
+ adPersonalizationActivityOnCreateMatch.mutableMethod.apply {
+ val initializeSettingsIndex = implementation!!.instructions.indexOfFirst {
+ it.opcode == Opcode.INVOKE_SUPER
+ } + 1
+
+ val thisRegister = getInstruction(initializeSettingsIndex - 1).registerC
+ val usableRegister = implementation!!.registerCount - parameters.size - 2
+
+ addInstructionsWithLabels(
+ initializeSettingsIndex,
+ """
+ invoke-static {v$thisRegister}, $initializeSettingsMethodDescriptor
+ move-result v$usableRegister
+ if-eqz v$usableRegister, :do_not_open
+ return-void
+ """,
+ ExternalLabel("do_not_open", getInstruction(initializeSettingsIndex)),
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/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
similarity index 58%
rename from src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt
index 1c40b5d9c..408958179 100644
--- a/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,44 +1,46 @@
package app.revanced.patches.tiktok.misc.spoof.sim
-import app.revanced.patcher.data.BytecodeContext
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.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
-import app.revanced.patches.tiktok.misc.integrations.IntegrationsPatch
-import app.revanced.patches.tiktok.misc.settings.SettingsPatch
-import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
+import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
+import app.revanced.patches.twitch.misc.settings.settingsPatch
import app.revanced.util.findMutableMethodOf
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
-@Patch(
+@Suppress("unused")
+val spoofSimPatch = bytecodePatch(
name = "SIM spoof",
description = "Spoofs the information which is retrieved from the SIM card.",
- dependencies = [IntegrationsPatch::class, SettingsPatch::class],
- compatiblePackages = [
- CompatiblePackage("com.ss.android.ugc.trill"),
- CompatiblePackage("com.zhiliaoapp.musically"),
- ],
use = false,
-)
-@Suppress("unused")
-object SpoofSimPatch : BytecodePatch(emptySet()) {
- private val replacements = hashMapOf(
- "getSimCountryIso" to "getCountryIso",
- "getNetworkCountryIso" to "getCountryIso",
- "getSimOperator" to "getOperator",
- "getNetworkOperator" to "getOperator",
- "getSimOperatorName" to "getOperatorName",
- "getNetworkOperatorName" to "getOperatorName",
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ settingsPatch,
)
- override fun execute(context: BytecodeContext) {
+ compatibleWith(
+ "com.ss.android.ugc.trill",
+ "com.zhiliaoapp.musically",
+ )
+
+ val settingsStatusLoadMatch by settingsStatusLoadFingerprint()
+
+ execute { context ->
+ val replacements = hashMapOf(
+ "getSimCountryIso" to "getCountryIso",
+ "getNetworkCountryIso" to "getCountryIso",
+ "getSimOperator" to "getOperator",
+ "getNetworkOperator" to "getOperator",
+ "getSimOperatorName" to "getOperatorName",
+ "getNetworkOperatorName" to "getOperatorName",
+ )
+
// Find all api call to check sim information.
buildMap {
context.classes.forEach { classDef ->
@@ -74,7 +76,17 @@ object SpoofSimPatch : BytecodePatch(emptySet()) {
with(findMutableMethodOf(method)) {
while (!patches.isEmpty()) {
val (index, replacement) = patches.removeLast()
- replaceReference(index, replacement)
+
+ 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
+ """,
+ )
}
}
}
@@ -82,24 +94,9 @@ object SpoofSimPatch : BytecodePatch(emptySet()) {
}
// Enable patch in settings.
- with(SettingsStatusLoadFingerprint.result!!.mutableMethod) {
- addInstruction(
- 0,
- "invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableSimSpoof()V",
- )
- }
- }
-
- // Patch Android API and return fake sim information.
- private fun MutableMethod.replaceReference(index: Int, replacement: String) {
- val resultReg = getInstruction(index + 1).registerA
-
- addInstructions(
- index + 2,
- """
- invoke-static {v$resultReg}, Lapp/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String;
- move-result-object v$resultReg
- """,
+ settingsStatusLoadMatch.mutableMethod.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
new file mode 100644
index 000000000..3e98d213e
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt
@@ -0,0 +1,30 @@
+package app.revanced.patches.tiktok.shared
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val getEnterFromFingerprint = fingerprint {
+ returns("Ljava/lang/String;")
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ parameters("Z")
+ opcodes(
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.MOVE_RESULT,
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.MOVE_RESULT_OBJECT,
+ Opcode.INVOKE_STATIC,
+ 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;")
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt
new file mode 100644
index 000000000..4a02c6221
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt
@@ -0,0 +1,25 @@
+package app.revanced.patches.trakt
+
+import app.revanced.patcher.fingerprint
+
+internal val isVIPEPFingerprint = fingerprint {
+ custom { method, classDef ->
+ if (!classDef.endsWith("RemoteUser;")) return@custom false
+
+ method.name == "isVIPEP"
+ }
+}
+
+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;")
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt
new file mode 100644
index 000000000..4b3138d67
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt
@@ -0,0 +1,36 @@
+package app.revanced.patches.trakt
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.util.exception
+
+@Suppress("unused")
+val unlockProPatch = bytecodePatch(
+ name = "Unlock pro",
+) {
+ compatibleWith("tv.trakt.trakt"("1.1.1"))
+
+ val remoteUserMatch by remoteUserFingerprint()
+
+ execute { context ->
+ remoteUserMatch.classDef.let { remoteUserClass ->
+ arrayOf(isVIPFingerprint, isVIPEPFingerprint).onEach { fingerprint ->
+ // Resolve both fingerprints on the same class.
+ if (!fingerprint.match(context, remoteUserClass)) {
+ throw fingerprint.exception
+ }
+ }.forEach { fingerprint ->
+ // Return true for both VIP check methods.
+ fingerprint.match?.mutableMethod?.addInstructions(
+ 0,
+ """
+ const/4 v0, 0x1
+ invoke-static {v0}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
+ move-result-object v1
+ return-object v1
+ """,
+ ) ?: throw fingerprint.exception
+ }
+ }
+ }
+}
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
new file mode 100644
index 000000000..12ffa15c1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt
@@ -0,0 +1,15 @@
+package app.revanced.patches.tudortmund.lockscreen
+
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
+
+internal val brightnessFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC)
+ returns("V")
+ parameters()
+ custom { method, classDef ->
+ method.name == "run" &&
+ method.definingClass.contains("/ScreenPlugin\$") &&
+ classDef.fields.any { it.type == "Ljava/lang/Float;" }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt
similarity index 70%
rename from src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt
index 368879cfa..c8121f705 100644
--- a/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt
@@ -1,36 +1,35 @@
-package app.revanced.patches.tudortmund.lockscreen.patch
+package app.revanced.patches.tudortmund.lockscreen
-import app.revanced.util.exception
-import app.revanced.patcher.data.BytecodeContext
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.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patches.tudortmund.lockscreen.fingerprints.BrightnessFingerprint
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.tudortmund.misc.extension.sharedExtensionPatch
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.reference.MethodReference
-@Patch(
+internal const val EXTENSION_CLASS_DESCRIPTOR =
+ "Lapp/revanced/extension/tudortmund/lockscreen/ShowOnLockscreenPatch;"
+
+@Suppress("unused")
+val showOnLockscreenPatch = bytecodePatch(
name = "Show on lockscreen",
description = "Shows student id and student ticket on lockscreen.",
- compatiblePackages = [CompatiblePackage("de.tudortmund.app")],
- requiresIntegrations = true
-)
-@Suppress("unused")
-object ShowOnLockscreenPatch : BytecodePatch(
- setOf(BrightnessFingerprint)
) {
- private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/tudortmund/lockscreen/ShowOnLockscreenPatch;"
+ dependsOn(sharedExtensionPatch)
- override fun execute(context: BytecodeContext) {
- BrightnessFingerprint.result?.mutableMethod?.apply {
+ compatibleWith("de.tudortmund.app")
+
+ val brightnessMatch by brightnessFingerprint()
+
+ execute {
+ brightnessMatch.mutableMethod.apply {
// Find the instruction where the brightness value is loaded into a register
- val brightnessInstruction = implementation!!.instructions.firstNotNullOf { instruction ->
+ val brightnessInstruction = instructions.firstNotNullOf { instruction ->
if (instruction.opcode != Opcode.IGET_OBJECT) return@firstNotNullOf null
val getInstruction = instruction as Instruction22c
@@ -45,29 +44,30 @@ object ShowOnLockscreenPatch : BytecodePatch(
// Gets the index of that instruction and the register of the Activity.
val (windowIndex, activityRegister) = implementation!!.instructions.withIndex()
.firstNotNullOf { (index, instruction) ->
- if (instruction.opcode != Opcode.INVOKE_VIRTUAL)
+ if (instruction.opcode != Opcode.INVOKE_VIRTUAL) {
return@firstNotNullOf null
+ }
val invokeInstruction = instruction as Instruction35c
val methodRef = invokeInstruction.reference as MethodReference
- if (methodRef.name != "getWindow" || methodRef.returnType != "Landroid/view/Window;")
+ if (methodRef.name != "getWindow" || methodRef.returnType != "Landroid/view/Window;") {
return@firstNotNullOf null
+ }
Pair(index, invokeInstruction.registerC)
}
// The register in which the brightness value is loaded
val brightnessRegister = brightnessInstruction.registerA
-
// Replaces the getWindow call with our custom one to run the lockscreen code
replaceInstruction(
windowIndex,
"invoke-static { v$activityRegister, v$brightnessRegister }, " +
- "$INTEGRATIONS_CLASS_DESCRIPTOR->" +
- "getWindow" +
- "(Landroidx/appcompat/app/AppCompatActivity;F)" +
- "Landroid/view/Window;"
+ "$EXTENSION_CLASS_DESCRIPTOR->" +
+ "getWindow" +
+ "(Landroidx/appcompat/app/AppCompatActivity;F)" +
+ "Landroid/view/Window;",
)
// Normally, the brightness is loaded into a register after the getWindow call.
@@ -79,11 +79,10 @@ object ShowOnLockscreenPatch : BytecodePatch(
"""
invoke-virtual { v$brightnessRegister }, Ljava/lang/Float;->floatValue()F
move-result v$brightnessRegister
- """
+ """,
)
addInstruction(windowIndex, brightnessInstruction)
-
- } ?: throw BrightnessFingerprint.exception
+ }
}
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/tudortmund/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tudortmund/misc/extension/ExtensionPatch.kt
new file mode 100644
index 000000000..1c056b0c6
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/misc/extension/ExtensionPatch.kt
@@ -0,0 +1,5 @@
+package app.revanced.patches.tudortmund.misc.extension
+
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch()
diff --git a/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt
similarity index 66%
rename from src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt
rename to patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt
index 77d31b9f3..4a9bbe297 100644
--- a/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt
@@ -1,20 +1,19 @@
package app.revanced.patches.tumblr.ads
-import app.revanced.patcher.data.BytecodeContext
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
-import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.tumblr.timelinefilter.addTimelineObjectTypeFilter
+import app.revanced.patches.tumblr.timelinefilter.filterTimelineObjectsPatch
-@Patch(
+@Suppress("unused")
+val disableDashboardAdsPatch = bytecodePatch(
name = "Disable dashboard ads",
description = "Disables ads in the dashboard.",
- compatiblePackages = [CompatiblePackage("com.tumblr")],
- dependencies = [TimelineFilterPatch::class],
-)
-@Suppress("unused")
-object DisableDashboardAds : BytecodePatch(emptySet()) {
- override fun execute(context: BytecodeContext) {
+) {
+ dependsOn(filterTimelineObjectsPatch)
+
+ compatibleWith("com.tumblr")
+
+ execute {
// 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(
@@ -31,7 +30,7 @@ object DisableDashboardAds : BytecodePatch(emptySet()) {
"FACEBOOK_BIDDAABLE", // "facebook_biddable_sdk_ad"
"GOOGLE_NATIVE", // "google_native_ad"
).forEach {
- TimelineFilterPatch.addObjectTypeFilter(it)
+ addTimelineObjectTypeFilter(it)
}
}
}
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
new file mode 100644
index 000000000..c166cb83b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt
@@ -0,0 +1,20 @@
+package app.revanced.patches.tumblr.annoyances.adfree
+
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.tumblr.featureflags.addFeatureFlagOverride
+import app.revanced.patches.tumblr.featureflags.overrideFeatureFlagsPatch
+
+@Suppress("unused")
+val disableAdFreeBannerPatch = bytecodePatch(
+ name = "Disable Ad-Free Banner",
+ description = "Disables the banner with a frog, prompting you to buy Tumblr Ad-Free.",
+) {
+ dependsOn(overrideFeatureFlagsPatch)
+
+ compatibleWith("com.tumblr")
+
+ execute {
+ // 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
new file mode 100644
index 000000000..3a46c19b2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.tumblr.annoyances.inappupdate
+
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.tumblr.featureflags.addFeatureFlagOverride
+import app.revanced.patches.tumblr.featureflags.overrideFeatureFlagsPatch
+
+@Suppress("unused")
+val disableInAppUpdatePatch = bytecodePatch(
+ name = "Disable in-app update",
+ description = "Disables the in-app update check and update prompt.",
+) {
+ dependsOn(overrideFeatureFlagsPatch)
+
+ compatibleWith("com.tumblr")
+
+ execute {
+ // 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
new file mode 100644
index 000000000..fbd7cf712
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt
@@ -0,0 +1,25 @@
+package app.revanced.patches.tumblr.annoyances.notifications
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableBlogNotificationReminderPatch = bytecodePatch(
+ name = "Disable blog notification reminder",
+ description = "Disables the reminder to enable notifications for blogs you visit.",
+) {
+ compatibleWith("com.tumblr")
+
+ val isBlogNotifyEnabledMatch by isBlogNotifyEnabledFingerprint()
+
+ execute {
+ isBlogNotifyEnabledMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ # Return false for BlogNotifyCtaDialog.isEnabled() method.
+ const/4 v0, 0x0
+ return v0
+ """,
+ )
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/fingerprints/IsBlogNotifyEnabledFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt
similarity index 55%
rename from src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/fingerprints/IsBlogNotifyEnabledFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt
index 55b67f2fa..67e051a7b 100644
--- a/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/fingerprints/IsBlogNotifyEnabledFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt
@@ -1,9 +1,11 @@
-package app.revanced.patches.tumblr.annoyances.notifications.fingerprints
+package app.revanced.patches.tumblr.annoyances.notifications
-import app.revanced.patcher.fingerprint.MethodFingerprint
+import app.revanced.patcher.fingerprint
// 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 object IsBlogNotifyEnabledFingerprint : MethodFingerprint(strings = listOf("isEnabled --> ", "blog_notify_enabled"))
\ No newline at end of file
+internal val isBlogNotifyEnabledFingerprint = fingerprint {
+ strings("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
new file mode 100644
index 000000000..7f1bc208f
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt
@@ -0,0 +1,18 @@
+package app.revanced.patches.tumblr.annoyances.popups
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val disableGiftMessagePopupPatch = bytecodePatch(
+ name = "Disable gift message popup",
+ description = "Disables the popup suggesting to buy TumblrMart items for other people.",
+) {
+ compatibleWith("com.tumblr")
+
+ val showGiftMessagePopupMatch by showGiftMessagePopupFingerprint()
+
+ execute {
+ showGiftMessagePopupMatch.mutableMethod.addInstructions(0, "return-void")
+ }
+}
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
new file mode 100644
index 000000000..7d00f2f1b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt
@@ -0,0 +1,11 @@
+package app.revanced.patches.tumblr.annoyances.popups
+
+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 {
+ accessFlags(AccessFlags.FINAL, AccessFlags.PUBLIC)
+ returns("V")
+ strings("activity", "anchorView", "textMessage")
+}
diff --git a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/fingerprints/GetFeatureValueFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt
similarity index 66%
rename from src/main/kotlin/app/revanced/patches/tumblr/featureflags/fingerprints/GetFeatureValueFingerprint.kt
rename to patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt
index 0088c3aa6..e24ca2884 100644
--- a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/fingerprints/GetFeatureValueFingerprint.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt
@@ -1,9 +1,8 @@
-package app.revanced.patches.tumblr.featureflags.fingerprints
+package app.revanced.patches.tumblr.featureflags
-import app.revanced.patcher.extensions.or
-import app.revanced.patcher.fingerprint.MethodFingerprint
-import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
+import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.patcher.fingerprint
// This fingerprint 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.
@@ -14,14 +13,14 @@ import com.android.tools.smali.dexlib2.Opcode
// 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 object GetFeatureValueFingerprint : MethodFingerprint(
- strings = listOf("feature"),
- opcodes = listOf(
+internal val getFeatureValueFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Ljava/lang/String;")
+ parameters("L", "Z")
+ opcodes(
Opcode.IF_EQZ,
Opcode.INVOKE_STATIC,
- Opcode.MOVE_RESULT
- ),
- returnType = "Ljava/lang/String;",
- parameters = listOf("L", "Z"),
- accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL
-)
\ No newline at end of file
+ Opcode.MOVE_RESULT,
+ )
+ strings("feature")
+}
diff --git a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt
similarity index 61%
rename from src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt
index 94367009c..e3d420e93 100644
--- a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt
@@ -1,49 +1,47 @@
package app.revanced.patches.tumblr.featureflags
-import app.revanced.util.exception
-import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
-import app.revanced.patcher.extensions.or
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
-import app.revanced.patches.tumblr.featureflags.fingerprints.GetFeatureValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
-@Patch(description = "Forcibly set the value of A/B testing features of your choice.")
-object OverrideFeatureFlagsPatch : BytecodePatch(
- setOf(GetFeatureValueFingerprint)
-) {
- /**
- * Override a feature flag with a value.
- *
- * @param name The name of the feature flag to override.
- * @param value The value to override the feature flag with.
- */
- @Suppress("KDocUnresolvedReference")
- internal lateinit var addOverride: (name: String, value: String) -> Unit private set
+/**
+ * Override a feature flag with a value.
+ *
+ * @param name The name of the feature flag to override.
+ * @param value The value to override the feature flag with.
+ */
+@Suppress("KDocUnresolvedReference")
+internal lateinit var addFeatureFlagOverride: (name: String, value: String) -> Unit
+ private set
- override fun execute(context: BytecodeContext) = GetFeatureValueFingerprint.result?.let {
- val configurationClass = it.method.definingClass
- val featureClass = it.method.parameterTypes[0].toString()
+@Suppress("unused")
+val overrideFeatureFlagsPatch = bytecodePatch(
+ description = "Forcibly set the value of A/B testing features of your choice.",
+) {
+ val getFeatureValueMatch by getFeatureValueFingerprint()
+
+ execute {
+ val configurationClass = getFeatureValueMatch.method.definingClass
+ val featureClass = getFeatureValueMatch.method.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 integration since the unused variable would get compiled away and the method would
+ // 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(
- it.method.definingClass,
+ getFeatureValueMatch.method.definingClass,
"getValueOverride",
listOf(ImmutableMethodParameter(featureClass, null, "feature")),
"Ljava/lang/String;",
- AccessFlags.PUBLIC or AccessFlags.FINAL,
+ AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
null,
null,
- MutableMethodImplementation(4)
+ MutableMethodImplementation(4),
).toMutable().apply {
// This is the equivalent of
// String featureName = feature.toString()
@@ -63,36 +61,36 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
# If none of the overrides returned a value, we should return null
const/4 v0, 0x0
return-object v0
- """
+ """,
)
}.also { helperMethod ->
- it.mutableClass.methods.add(helperMethod)
+ getFeatureValueMatch.mutableClass.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 = it.scanResult.patternScanResult!!.startIndex
- it.mutableMethod.addInstructionsWithLabels(
+ val getFeatureIndex = getFeatureValueMatch.patternMatch!!.startIndex
+ getFeatureValueMatch.mutableMethod.addInstructionsWithLabels(
getFeatureIndex,
"""
- # Call the Helper Method with the Feature
- invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String;
- move-result-object v0
- # If it returned null, skip
- if-eqz v0, :is_null
- # If it didnt return null, return that string
- return-object v0
-
- # If our override helper returned null, we let the function continue normally
- :is_null
- nop
- """
+ # Call the Helper Method with the Feature
+ invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String;
+ move-result-object v0
+ # If it returned null, skip
+ if-eqz v0, :is_null
+ # If it didnt return null, return that string
+ return-object v0
+
+ # If our override helper returned null, we let the function continue normally
+ :is_null
+ nop
+ """,
)
val helperInsertIndex = 2
- addOverride = { name, value ->
+ 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
@@ -112,8 +110,8 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
# Else we just continue...
:no_override
nop
- """
+ """,
)
}
- } ?: throw GetFeatureValueFingerprint.exception
-}
\ No newline at end of file
+ }
+}
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
new file mode 100644
index 000000000..11616fcc9
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt
@@ -0,0 +1,23 @@
+package app.revanced.patches.tumblr.fixes
+
+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: ")
+}
+
+// Fingerprint for 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 {
+ 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
new file mode 100644
index 000000000..d51aead2b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt
@@ -0,0 +1,57 @@
+package app.revanced.patches.tumblr.fixes
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+
+@Suppress("unused")
+val fixOldVersionsPatch = bytecodePatch(
+ name = "Fix old versions",
+ description = "Fixes old versions of the app (v33.2 and earlier) breaking due to Tumblr removing remnants of Tumblr" +
+ " Live from the API, which causes many requests to fail. This patch has no effect on newer versions of the app.",
+ use = false,
+) {
+ compatibleWith("com.tumblr")
+
+ val httpPathParserMatch by httpPathParserFingerprint()
+ val addQueryParamMatch by addQueryParamFingerprint()
+
+ execute {
+ val liveQueryParameters = listOf(
+ ",?live_now",
+ ",?live_streaming_user_id",
+ )
+
+ // Remove the live query parameters from the path when it's specified via a @METHOD annotation.
+ for (liveQueryParameter in liveQueryParameters) {
+ httpPathParserMatch.mutableMethod.addInstructions(
+ httpPathParserMatch.patternMatch!!.endIndex + 1,
+ """
+ # urlPath = urlPath.replace(liveQueryParameter, "")
+ const-string p1, "$liveQueryParameter"
+ const-string p3, ""
+ invoke-virtual {p2, p1, p3}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
+ move-result-object p2
+ """,
+ )
+ }
+
+ // Remove the live query parameters when passed via a parameter which has the @Query annotation.
+ // e.g. an API call could be defined like this:
+ // @GET("api/me/info")
+ // ApiResponse getCurrentUserInfo(@Query("fields[blog]") String value)
+ // 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) {
+ addQueryParamMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ # queryParameterValue = queryParameterValue.replace(liveQueryParameter, "")
+ const-string v0, "$liveQueryParameter"
+ const-string v1, ""
+ invoke-virtual {p2, v0, v1}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
+ move-result-object p2
+ """,
+ )
+ }
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt
new file mode 100644
index 000000000..64fa2ed79
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt
@@ -0,0 +1,29 @@
+package app.revanced.patches.tumblr.live
+
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.tumblr.featureflags.addFeatureFlagOverride
+import app.revanced.patches.tumblr.featureflags.overrideFeatureFlagsPatch
+import app.revanced.patches.tumblr.timelinefilter.addTimelineObjectTypeFilter
+import app.revanced.patches.tumblr.timelinefilter.filterTimelineObjectsPatch
+
+@Suppress("unused")
+@Deprecated("Tumblr Live was removed and is no longer served in the feed, making this patch useless.")
+val disableTumblrLivePatch = bytecodePatch(
+ description = "Disable the Tumblr Live tab button and dashboard carousel.",
+) {
+ dependsOn(
+ overrideFeatureFlagsPatch,
+ filterTimelineObjectsPatch,
+ )
+
+ compatibleWith("com.tumblr")
+
+ execute {
+ // Hide the LIVE_MARQUEE timeline element that appears in the feed
+ // Called "live_marquee" in api response
+ addTimelineObjectTypeFilter("LIVE_MARQUEE")
+
+ // Hide the Tab button for Tumblr Live by forcing the feature flag to false
+ addFeatureFlagOverride("liveStreaming", "false")
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/misc/extension/ExtensionPatch.kt
new file mode 100644
index 000000000..3a69b74a5
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/misc/extension/ExtensionPatch.kt
@@ -0,0 +1,5 @@
+package app.revanced.patches.tumblr.misc.extension
+
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch()
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
new file mode 100644
index 000000000..ff44e1bdb
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt
@@ -0,0 +1,67 @@
+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.patch.bytecodePatch
+import app.revanced.patches.tumblr.misc.extension.sharedExtensionPatch
+import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c
+
+/**
+ * Add a filter to hide the given timeline object type.
+ * The list of all Timeline object types is found in the TimelineObjectType class,
+ * where they are mapped from their api name (returned by tumblr via the HTTP API) to the enum value name.
+ *
+ * @param typeName The enum name of the timeline object type to hide.
+ */
+@Suppress("KDocUnresolvedReference")
+lateinit var addTimelineObjectTypeFilter: (typeName: String) -> Unit
+
+@Suppress("unused")
+val filterTimelineObjectsPatch = bytecodePatch(
+ description = "Filter timeline objects.",
+) {
+ dependsOn(sharedExtensionPatch)
+
+ val timelineConstructorMatch by timelineConstructorFingerprint()
+ val timelineFilterExtensionMatch by timelineFilterExtensionFingerprint()
+ val postsResponseConstructorMatch by postsResponseConstructorFingerprint()
+
+ execute {
+ val filterInsertIndex = timelineFilterExtensionMatch.patternMatch!!.startIndex
+
+ timelineFilterExtensionMatch.mutableMethod.apply {
+ val addInstruction = getInstruction(filterInsertIndex + 1)
+
+ val filterListRegister = addInstruction.registerC
+ val stringRegister = addInstruction.registerD
+
+ // Remove "BLOCKED_OBJECT_DUMMY"
+ removeInstructions(filterInsertIndex, 2)
+
+ addTimelineObjectTypeFilter = { typeName ->
+ // blockedObjectTypes.add({typeName})
+ addInstructionsWithLabels(
+ filterInsertIndex,
+ """
+ const-string v$stringRegister, "$typeName"
+ invoke-virtual { v$filterListRegister, v$stringRegister }, Ljava/util/HashSet;->add(Ljava/lang/Object;)Z
+ """,
+ )
+ }
+ }
+
+ mapOf(
+ timelineConstructorMatch to 1,
+ postsResponseConstructorMatch to 2,
+ ).forEach { (match, timelineObjectsRegister) ->
+ match.mutableMethod.addInstructions(
+ 0,
+ "invoke-static {p$timelineObjectsRegister}, " +
+ "Lapp/revanced/extension/tumblr/patches/TimelineFilterPatch;->" +
+ "filterTimeline(Ljava/util/List;)V",
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..b8ed3bfc4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt
@@ -0,0 +1,36 @@
+package app.revanced.patches.tumblr.timelinefilter
+
+import app.revanced.patcher.fingerprint
+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 {
+ accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PUBLIC)
+ custom { method, classDef -> classDef.endsWith("/PostsResponse;") && method.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;"
+ }
+}
+
+// This fingerprints the extension TimelineFilterPatch.filterTimeline method.
+// The opcode fingerprint is searching for
+// if ("BLOCKED_OBJECT_DUMMY".equals(elementType)) iterator.remove();
+internal val timelineFilterExtensionFingerprint = fingerprint {
+ 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
new file mode 100644
index 000000000..6827a99e3
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt
@@ -0,0 +1,48 @@
+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.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.extension.sharedExtensionPatch
+import app.revanced.patches.twitch.misc.settings.PreferenceScreen
+import app.revanced.patches.twitch.misc.settings.settingsPatch
+
+@Suppress("unused")
+val audioAdsPatch = bytecodePatch(
+ name = "Block audio ads",
+ description = "Blocks audio ads in streams and VODs.",
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ settingsPatch,
+ addResourcesPatch,
+ )
+
+ compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
+
+ val audioAdsPresenterPlayMatch by audioAdsPresenterPlayFingerprint()
+
+ execute {
+ addResources("twitch", "ad.audio.audioAdsPatch")
+
+ PreferenceScreen.ADS.CLIENT_SIDE.addPreferences(
+ SwitchPreference("revanced_block_audio_ads"),
+ )
+
+ // Block playAds call
+ audioAdsPresenterPlayMatch.mutableMethod.addInstructionsWithLabels(
+ 0,
+ """
+ invoke-static { }, Lapp/revanced/extension/twitch/patches/AudioAdsPatch;->shouldBlockAudioAds()Z
+ move-result v0
+ if-eqz v0, :show_audio_ads
+ return-void
+ """,
+ ExternalLabel("show_audio_ads", audioAdsPresenterPlayMatch.mutableMethod.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
new file mode 100644
index 000000000..21e9cb6d2
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.twitch.ad.audio
+
+import app.revanced.patcher.fingerprint
+
+internal val audioAdsPresenterPlayFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("AudioAdsPlayerPresenter;") && method.name == "playAd"
+ }
+}
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
new file mode 100644
index 000000000..4d36bc22d
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt
@@ -0,0 +1,44 @@
+package app.revanced.patches.twitch.ad.embedded
+
+import app.revanced.patcher.extensions.InstructionExtensions.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.misc.extension.sharedExtensionPatch
+import app.revanced.patches.twitch.misc.settings.PreferenceScreen
+import app.revanced.patches.twitch.misc.settings.settingsPatch
+
+@Suppress("unused")
+val embeddedAdsPatch = bytecodePatch(
+ name = "Block embedded ads",
+ description = "Blocks embedded stream ads using services like Luminous or PurpleAdBlocker.",
+) {
+ dependsOn(
+ videoAdsPatch,
+ sharedExtensionPatch,
+ settingsPatch,
+ )
+
+ compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
+
+ val createUsherClientMatch by createsUsherClientFingerprint()
+
+ execute {
+ addResources("twitch", "ad.embedded.embeddedAdsPatch")
+
+ PreferenceScreen.ADS.SURESTREAM.addPreferences(
+ ListPreference("revanced_block_embedded_ads", summaryKey = null),
+ )
+
+ // Inject OkHttp3 application interceptor
+ createUsherClientMatch.mutableMethod.addInstructions(
+ 3,
+ """
+ invoke-static {}, Lapp/revanced/extension/twitch/patches/EmbeddedAdsPatch;->createRequestInterceptor()Lapp/revanced/extension/twitch/api/RequestInterceptor;
+ move-result-object v2
+ invoke-virtual {v0, v2}, Lokhttp3/OkHttpClient${"$"}Builder;->addInterceptor(Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient${"$"}Builder;
+ """,
+ )
+ }
+}
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
new file mode 100644
index 000000000..cbf817469
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.twitch.ad.embedded
+
+import app.revanced.patcher.fingerprint
+
+internal val createsUsherClientFingerprint = fingerprint {
+ custom { method, _ ->
+ method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") && method.name == "buildOkHttpClient"
+ }
+}
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
new file mode 100644
index 000000000..48ecefba8
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt
@@ -0,0 +1,67 @@
+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.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,
+ skipLabelName: String,
+ block: BytecodePatchBuilder.(
+ createConditionInstructions: (register: String) -> String,
+ blockMethods: BytecodePatchContext.(
+ clazz: String,
+ methodNames: Set,
+ returnMethod: ReturnMethod,
+ ) -> Boolean,
+ ) -> Unit,
+) = bytecodePatch {
+ fun createConditionInstructions(register: String) = """
+ invoke-static { }, $conditionCall
+ move-result $register
+ if-eqz $register, :$skipLabelName
+ """
+
+ fun BytecodePatchContext.blockMethods(
+ classDefType: String,
+ methodNames: Set,
+ returnMethod: ReturnMethod,
+ ) = with(classBy { classDefType == it.type }?.mutableClass) {
+ this ?: return false
+
+ methods.filter { it.name in methodNames }.forEach {
+ val retInstruction = when (returnMethod.returnType) {
+ 'V' -> "return-void"
+ 'Z' ->
+ """
+ const/4 v0, ${returnMethod.value}
+ return v0
+ """
+
+ else -> throw NotImplementedError()
+ }
+
+ it.addInstructionsWithLabels(
+ 0,
+ """
+ ${createConditionInstructions("v0")}
+ $retInstruction
+ """,
+ ExternalLabel(skipLabelName, it.getInstruction(0)),
+ )
+ }
+
+ true
+ }
+
+ block(::createConditionInstructions, BytecodePatchContext::blockMethods)
+}
+
+class ReturnMethod(val returnType: Char, val value: String) {
+ companion object {
+ val default = ReturnMethod('V', "")
+ }
+}
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
new file mode 100644
index 000000000..d03449733
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt
@@ -0,0 +1,28 @@
+package app.revanced.patches.twitch.ad.video
+
+import app.revanced.patcher.fingerprint
+
+internal val checkAdEligibilityLambdaFingerprint = fingerprint {
+ returns("Lio/reactivex/Single;")
+ parameters("L")
+ custom { method, _ ->
+ method.definingClass.endsWith("/AdEligibilityFetcher;") &&
+ method.name == "shouldRequestAd"
+ }
+}
+
+internal val contentConfigShowAdsFingerprint = fingerprint {
+ returns("Z")
+ parameters()
+ custom { method, _ ->
+ method.definingClass.endsWith("/ContentConfigData;") && method.name == "getShowAds"
+ }
+}
+
+internal val getReadyToShowAdFingerprint = fingerprint {
+ returns("Ltv/twitch/android/core/mvp/presenter/StateAndAction;")
+ parameters("L", "L")
+ custom { method, _ ->
+ method.definingClass.endsWith("/StreamDisplayAdsPresenter;") && method.name == "getReadyToShowAdOrAbort"
+ }
+}
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
new file mode 100644
index 000000000..5c0eed6eb
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt
@@ -0,0 +1,167 @@
+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.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.ad.shared.util.ReturnMethod
+import app.revanced.patches.twitch.ad.shared.util.adPatch
+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(
+ name = "Block video ads",
+ description = "Blocks video ads in streams and VODs.",
+) {
+ val conditionCall = "Lapp/revanced/extension/twitch/patches/VideoAdsPatch;->shouldBlockVideoAds()Z"
+ val skipLabelName = "show_video_ads"
+
+ dependsOn(
+ sharedExtensionPatch,
+ settingsPatch,
+ addResourcesPatch,
+ adPatch(conditionCall, skipLabelName) { createConditionInstructions, blockMethods ->
+ val checkAdEligibilityLambdaMatch by checkAdEligibilityLambdaFingerprint()
+ val getReadyToShowAdMatch by getReadyToShowAdFingerprint()
+ val contentConfigShowAdsMatch by contentConfigShowAdsFingerprint()
+
+ execute { context ->
+ addResources("twitch", "ad.video.videoAdsPatch")
+
+ PreferenceScreen.ADS.CLIENT_SIDE.addPreferences(
+ SwitchPreference("revanced_block_video_ads"),
+ )
+
+ /* Amazon ads SDK */
+ context.blockMethods(
+ "Lcom/amazon/ads/video/player/AdsManagerImpl;",
+ setOf("playAds"),
+ ReturnMethod.default,
+ )
+
+ /* Twitch ads manager */
+ context.blockMethods(
+ "Ltv/twitch/android/shared/ads/VideoAdManager;",
+ setOf(
+ "checkAdEligibilityAndRequestAd",
+ "requestAd",
+ "requestAds",
+ ),
+ ReturnMethod.default,
+ )
+
+ /* Various ad presenters */
+ context.blockMethods(
+ "Ltv/twitch/android/shared/ads/AdsPlayerPresenter;",
+ setOf(
+ "requestAd",
+ "requestFirstAd",
+ "requestFirstAdIfEligible",
+ "requestMidroll",
+ "requestAdFromMultiAdFormatEvent",
+ ),
+ ReturnMethod.default,
+ )
+
+ context.blockMethods(
+ "Ltv/twitch/android/shared/ads/AdsVodPlayerPresenter;",
+ setOf(
+ "requestAd",
+ "requestFirstAd",
+ ),
+ ReturnMethod.default,
+ )
+
+ context.blockMethods(
+ "Ltv/twitch/android/feature/theatre/ads/AdEdgeAllocationPresenter;",
+ setOf(
+ "parseAdAndCheckEligibility",
+ "requestAdsAfterEligibilityCheck",
+ "showAd",
+ "bindMultiAdFormatAllocation",
+ ),
+ ReturnMethod.default,
+ )
+
+ /* A/B ad testing experiments */
+ context.blockMethods(
+ "Ltv/twitch/android/provider/experiments/helpers/DisplayAdsExperimentHelper;",
+ setOf("areDisplayAdsEnabled"),
+ ReturnMethod('Z', "0"),
+ )
+
+ context.blockMethods(
+ "Ltv/twitch/android/shared/ads/tracking/MultiFormatAdsTrackingExperiment;",
+ setOf(
+ "shouldUseMultiAdFormatTracker",
+ "shouldUseVideoAdTracker",
+ ),
+ ReturnMethod('Z', "0"),
+ )
+
+ context.blockMethods(
+ "Ltv/twitch/android/shared/ads/MultiformatAdsExperiment;",
+ setOf(
+ "shouldDisableClientSideLivePreroll",
+ "shouldDisableClientSideVodPreroll",
+ ),
+ ReturnMethod('Z', "1"),
+ )
+
+ // Pretend our player is ineligible for all ads.
+ checkAdEligibilityLambdaMatch.mutableMethod.addInstructionsWithLabels(
+ 0,
+ """
+ ${createConditionInstructions("v0")}
+ const/4 v0, 0
+ invoke-static {v0}, Lio/reactivex/Single;->just(Ljava/lang/Object;)Lio/reactivex/Single;
+ move-result-object p0
+ return-object p0
+ """,
+ ExternalLabel(
+ skipLabelName,
+ checkAdEligibilityLambdaMatch.mutableMethod.getInstruction(0),
+ ),
+ )
+
+ val adFormatDeclined =
+ "Ltv/twitch/android/shared/display/ads/theatre/StreamDisplayAdsPresenter\$Action\$AdFormatDeclined;"
+ getReadyToShowAdMatch.mutableMethod.addInstructionsWithLabels(
+ 0,
+ """
+ ${createConditionInstructions("v0")}
+ sget-object p2, $adFormatDeclined->INSTANCE:$adFormatDeclined
+ invoke-static {p1, p2}, Ltv/twitch/android/core/mvp/presenter/StateMachineKt;->plus(Ltv/twitch/android/core/mvp/presenter/PresenterState;Ltv/twitch/android/core/mvp/presenter/PresenterAction;)Ltv/twitch/android/core/mvp/presenter/StateAndAction;
+ move-result-object p1
+ return-object p1
+ """,
+ ExternalLabel(skipLabelName, getReadyToShowAdMatch.mutableMethod.getInstruction(0)),
+ )
+
+ // Spoof showAds JSON field.
+ contentConfigShowAdsMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ ${createConditionInstructions("v0")}
+ const/4 v0, 0
+ :$skipLabelName
+ return v0
+ """,
+ )
+ }
+ },
+ )
+
+ compatibleWith(
+ "tv.twitch.android.app"(
+ "15.4.1",
+ "16.1.0",
+ "16.9.1",
+ ),
+ )
+}
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
new file mode 100644
index 000000000..21da99a5b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt
@@ -0,0 +1,24 @@
+package app.revanced.patches.twitch.chat.antidelete
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val chatUtilCreateDeletedSpanFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("ChatUtil\$Companion;") && method.name == "createDeletedSpanFromChatMessageSpan"
+ }
+}
+
+internal val deletedMessageClickableSpanCtorFingerprint = fingerprint {
+ 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"
+ }
+}
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
new file mode 100644
index 000000000..93996e77a
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt
@@ -0,0 +1,78 @@
+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.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
+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.",
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ settingsPatch,
+ addResourcesPatch,
+ )
+
+ compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
+
+ fun createSpoilerConditionInstructions(register: String = "v0") = """
+ invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z
+ move-result $register
+ if-eqz $register, :no_spoiler
+ """
+
+ val setHasModAccessMatch by setHasModAccessFingerprint()
+ val deletedMessageClickableSpanCtorMatch by deletedMessageClickableSpanCtorFingerprint()
+ val chatUtilCreateDeletedSpanMatch by chatUtilCreateDeletedSpanFingerprint()
+
+ execute {
+ addResources("twitch", "chat.antidelete.showDeletedMessagesPatch")
+
+ PreferenceScreen.CHAT.GENERAL.addPreferences(
+ ListPreference(
+ key = "revanced_show_deleted_messages",
+ summaryKey = null,
+ ),
+ )
+
+ // Spoiler mode: Force set hasModAccess member to true in constructor
+ deletedMessageClickableSpanCtorMatch.mutableMethod.apply {
+ addInstructionsWithLabels(
+ implementation!!.instructions.lastIndex, /* place in front of return-void */
+ """
+ ${createSpoilerConditionInstructions()}
+ const/4 v0, 1
+ iput-boolean v0, p0, $definingClass->hasModAccess:Z
+ """,
+ ExternalLabel("no_spoiler", getInstruction(implementation!!.instructions.lastIndex)),
+ )
+ }
+
+ // Spoiler mode: Disable setHasModAccess setter
+ setHasModAccessMatch.mutableMethod.addInstruction(0, "return-void")
+
+ // Cross-out mode: Reformat span of deleted message
+ chatUtilCreateDeletedSpanMatch.mutableMethod.apply {
+ addInstructionsWithLabels(
+ 0,
+ """
+ invoke-static {p2}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->reformatDeletedMessage(Landroid/text/Spanned;)Landroid/text/Spanned;
+ move-result-object v0
+ if-eqz v0, :no_reformat
+ return-object v0
+ """,
+ ExternalLabel("no_reformat", getInstruction(0)),
+ )
+ }
+ }
+}
diff --git a/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt
similarity index 50%
rename from src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt
rename to patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt
index 740f8cedf..15a03f7c3 100644
--- a/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,41 +1,42 @@
package app.revanced.patches.twitch.chat.autoclaim
-import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
-import app.revanced.patcher.patch.BytecodePatch
-import app.revanced.patcher.patch.annotation.CompatiblePackage
-import app.revanced.patcher.patch.annotation.Patch
+import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
-import app.revanced.patches.all.misc.resources.AddResourcesPatch
+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.chat.autoclaim.fingerprints.CommunityPointsButtonViewDelegateFingerprint
-import app.revanced.patches.twitch.misc.settings.SettingsPatch
-import app.revanced.util.exception
+import app.revanced.patches.twitch.misc.settings.PreferenceScreen
+import app.revanced.patches.twitch.misc.settings.settingsPatch
-@Patch(
+@Suppress("unused")
+val autoClaimChannelPointsPatch = bytecodePatch(
name = "Auto claim channel points",
description = "Automatically claim Channel Points.",
- dependencies = [SettingsPatch::class, AddResourcesPatch::class],
- compatiblePackages = [CompatiblePackage("tv.twitch.android.app", ["15.4.1", "16.1.0", "16.9.1"])]
-)
-@Suppress("unused")
-object AutoClaimChannelPointsPatch : BytecodePatch(
- setOf(CommunityPointsButtonViewDelegateFingerprint)
) {
- override fun execute(context: BytecodeContext) {
- AddResourcesPatch(this::class)
+ dependsOn(
+ settingsPatch,
+ addResourcesPatch,
+ )
- SettingsPatch.PreferenceScreen.CHAT.GENERAL.addPreferences(
- SwitchPreference("revanced_auto_claim_channel_points")
+ compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
+
+ val communityPointsButtonViewDelegateMatch by communityPointsButtonViewDelegateFingerprint()
+
+ execute {
+ addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch")
+
+ PreferenceScreen.CHAT.GENERAL.addPreferences(
+ SwitchPreference("revanced_auto_claim_channel_points"),
)
- CommunityPointsButtonViewDelegateFingerprint.result?.mutableMethod?.apply {
+ communityPointsButtonViewDelegateMatch.mutableMethod.apply {
val lastIndex = implementation!!.instructions.lastIndex
addInstructionsWithLabels(
lastIndex, // place in front of return-void
"""
- invoke-static {}, Lapp/revanced/integrations/twitch/patches/AutoClaimChannelPointsPatch;->shouldAutoClaim()Z
+ invoke-static {}, Lapp/revanced/extension/twitch/patches/AutoClaimChannelPointsPatch;->shouldAutoClaim()Z
move-result v0
if-eqz v0, :auto_claim
@@ -44,8 +45,8 @@ object AutoClaimChannelPointsPatch : BytecodePatch(
iget-object v0, p0, Ltv/twitch/android/shared/community/points/viewdelegate/CommunityPointsButtonViewDelegate;->buttonLayout:Landroid/view/ViewGroup;
invoke-virtual { v0 }, Landroid/view/View;->callOnClick()Z
""",
- ExternalLabel("auto_claim", getInstruction(lastIndex))
+ ExternalLabel("auto_claim", getInstruction(lastIndex)),
)
- } ?: throw CommunityPointsButtonViewDelegateFingerprint.exception
+ }
}
}
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
new file mode 100644
index 000000000..80abc9ac4
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt
@@ -0,0 +1,10 @@
+package app.revanced.patches.twitch.chat.autoclaim
+
+import app.revanced.patcher.fingerprint
+
+internal val communityPointsButtonViewDelegateFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("CommunityPointsButtonViewDelegate;") &&
+ method.name == "showClaimAvailable"
+ }
+}
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
new file mode 100644
index 000000000..25cd5acc1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt
@@ -0,0 +1,52 @@
+package app.revanced.patches.twitch.debug
+
+import app.revanced.patcher.extensions.InstructionExtensions.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.twitch.misc.extension.sharedExtensionPatch
+import app.revanced.patches.twitch.misc.settings.PreferenceScreen
+import app.revanced.patches.twitch.misc.settings.settingsPatch
+
+@Suppress("unused")
+val debugModePatch = bytecodePatch(
+ name = "Debug mode",
+ description = "Enables Twitch's internal debugging mode.",
+ use = false,
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ settingsPatch,
+ addResourcesPatch,
+ )
+
+ compatibleWith("tv.twitch.android.app")
+
+ val isDebugConfigEnabledMatch by isDebugConfigEnabledFingerprint()
+ val isOmVerificationEnabledMatch by isOmVerificationEnabledFingerprint()
+ val shouldShowDebugOptionsMatch by shouldShowDebugOptionsFingerprint()
+
+ execute {
+ addResources("twitch", "debug.debugModePatch")
+
+ PreferenceScreen.MISC.OTHER.addPreferences(
+ SwitchPreference("revanced_twitch_debug_mode"),
+ )
+
+ listOf(
+ isDebugConfigEnabledMatch,
+ isOmVerificationEnabledMatch,
+ shouldShowDebugOptionsMatch,
+ ).forEach {
+ it.mutableMethod.addInstructions(
+ 0,
+ """
+ invoke-static {}, Lapp/revanced/extension/twitch/patches/DebugModePatch;->isDebugModeEnabled()Z
+ move-result v0
+ return v0
+ """,
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000..665180c19
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt
@@ -0,0 +1,21 @@
+package app.revanced.patches.twitch.debug
+
+import app.revanced.patcher.fingerprint
+
+internal val isDebugConfigEnabledFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/BuildConfigUtil;") && method.name == "isDebugConfigEnabled"
+ }
+}
+
+internal val isOmVerificationEnabledFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/BuildConfigUtil;") && method.name == "isOmVerificationEnabled"
+ }
+}
+
+internal val shouldShowDebugOptionsFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/BuildConfigUtil;") && method.name == "shouldShowDebugOptions"
+ }
+}
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
new file mode 100644
index 000000000..9a46867ab
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt
@@ -0,0 +1,9 @@
+package app.revanced.patches.twitch.misc.extension
+
+import app.revanced.patches.shared.misc.extension.extensionHook
+
+internal val initHook = extensionHook {
+ custom { method, classDef ->
+ method.name == "onCreate" && classDef.endsWith("/TwitchApplication;")
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/SharedExtensionPatch.kt
new file mode 100644
index 000000000..2299d3bb5
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/SharedExtensionPatch.kt
@@ -0,0 +1,5 @@
+package app.revanced.patches.twitch.misc.extension
+
+import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
+
+val sharedExtensionPatch = sharedExtensionPatch(initHook)
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
new file mode 100644
index 000000000..43d5bb39b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt
@@ -0,0 +1,34 @@
+package app.revanced.patches.twitch.misc.settings
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+
+internal val menuGroupsOnClickFingerprint = fingerprint {
+ accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC, AccessFlags.FINAL)
+ returns("V")
+ parameters("L", "L", "L")
+ custom { method, classDef ->
+ classDef.endsWith("/SettingsMenuViewDelegate;") &&
+ method.name.contains("render")
+ }
+}
+
+internal val menuGroupsUpdatedFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/SettingsMenuPresenter\$Event\$MenuGroupsUpdated;") &&
+ method.name == ""
+ }
+}
+
+internal val settingsActivityOnCreateFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/SettingsActivity;") &&
+ method.name == "onCreate"
+ }
+}
+
+internal val settingsMenuItemEnumFingerprint = fingerprint {
+ custom { method, classDef ->
+ classDef.endsWith("/SettingsMenuItem;") && method.name == ""
+ }
+}
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
new file mode 100644
index 000000000..423687792
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt
@@ -0,0 +1,208 @@
+package app.revanced.patches.twitch.misc.settings
+
+import app.revanced.patcher.Match
+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.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.PreferenceCategory
+import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
+import app.revanced.patches.shared.misc.settings.settingsPatch
+import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.immutable.ImmutableField
+
+private const val REVANCED_SETTINGS_MENU_ITEM_NAME = "RevancedSettings"
+private const val REVANCED_SETTINGS_MENU_ITEM_ID = 0x7
+private const val REVANCED_SETTINGS_MENU_ITEM_TITLE_RES = "revanced_settings"
+private const val REVANCED_SETTINGS_MENU_ITEM_ICON_RES = "ic_settings"
+
+private const val MENU_ITEM_ENUM_CLASS_DESCRIPTOR = "Ltv/twitch/android/feature/settings/menu/SettingsMenuItem;"
+private const val MENU_DISMISS_EVENT_CLASS_DESCRIPTOR =
+ "Ltv/twitch/android/feature/settings/menu/SettingsMenuViewDelegate\$Event\$OnDismissClicked;"
+
+private const val EXTENSION_PACKAGE = "app/revanced/extension/twitch"
+private const val ACTIVITY_HOOKS_CLASS_DESCRIPTOR = "L$EXTENSION_PACKAGE/settings/AppCompatActivityHook;"
+private const val UTILS_CLASS_DESCRIPTOR = "L$EXTENSION_PACKAGE/Utils;"
+
+private val preferences = mutableSetOf()
+
+fun addSettingPreference(screen: BasePreference) {
+ preferences += screen
+}
+
+val settingsPatch = bytecodePatch(
+ name = "Settings",
+ description = "Adds settings menu to Twitch.",
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ addResourcesPatch,
+ settingsPatch(preferences = preferences),
+ )
+
+ compatibleWith(
+ "tv.twitch.android.app"(
+ "15.4.1",
+ "16.1.0",
+ "16.9.1",
+ ),
+ )
+
+ val settingsActivityOnCreateMatch by settingsActivityOnCreateFingerprint()
+ val settingsMenuItemEnumMatch by settingsMenuItemEnumFingerprint()
+ val menuGroupsUpdatedMatch by menuGroupsUpdatedFingerprint()
+ val menuGroupsOnClickMatch by menuGroupsOnClickFingerprint()
+
+ execute {
+ addResources("twitch", "misc.settings.settingsPatch")
+
+ PreferenceScreen.MISC.OTHER.addPreferences(
+ // The debug setting is shared across multiple apps and the key must be the same.
+ // But the title and summary must be different, otherwise when the strings file is flattened
+ // for Crowdin push, Crowdin gets confused by the duplicate keys.
+ // FIXME: Ideally the shared debug strings are extracted into a common app group
+ // and then both apps import that. But for now unique unique title and summary keys also works.
+ SwitchPreference(
+ key = "revanced_debug",
+ titleKey = "revanced_twitch_debug_title",
+ summaryOnKey = "revanced_twitch_debug_summary_on",
+ summaryOffKey = "revanced_twitch_debug_summary_off",
+ ),
+ )
+
+ // Hook onCreate to handle fragment creation.
+ val insertIndex = settingsActivityOnCreateMatch.mutableMethod.implementation!!.instructions.size - 2
+ settingsActivityOnCreateMatch.mutableMethod.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",
+ settingsActivityOnCreateMatch.mutableMethod.getInstruction(insertIndex),
+ ),
+ )
+
+ // Create new menu item for settings menu.
+ fun Match.injectMenuItem(
+ name: String,
+ value: Int,
+ titleResourceName: String,
+ iconResourceName: String,
+ ) {
+ // Add new static enum member field
+ mutableClass.staticFields.add(
+ ImmutableField(
+ mutableMethod.definingClass,
+ name,
+ MENU_ITEM_ENUM_CLASS_DESCRIPTOR,
+ AccessFlags.PUBLIC.value or
+ AccessFlags.FINAL.value or
+ AccessFlags.ENUM.value or
+ AccessFlags.STATIC.value,
+ null,
+ null,
+ null,
+ ).toMutable(),
+ )
+
+ // Add initializer for the new enum member
+ mutableMethod.addInstructions(
+ mutableMethod.implementation!!.instructions.size - 4,
+ """
+ new-instance v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR
+ const-string v1, "$titleResourceName"
+ invoke-static {v1}, $UTILS_CLASS_DESCRIPTOR->getStringId(Ljava/lang/String;)I
+ move-result v1
+ const-string v3, "$iconResourceName"
+ invoke-static {v3}, $UTILS_CLASS_DESCRIPTOR->getDrawableId(Ljava/lang/String;)I
+ move-result v3
+ const-string v4, "$name"
+ const/4 v5, $value
+ invoke-direct {v0, v4, v5, v1, v3}, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->(Ljava/lang/String;III)V
+ sput-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$name:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR
+ """,
+ )
+ }
+
+ settingsMenuItemEnumMatch.injectMenuItem(
+ REVANCED_SETTINGS_MENU_ITEM_NAME,
+ REVANCED_SETTINGS_MENU_ITEM_ID,
+ REVANCED_SETTINGS_MENU_ITEM_TITLE_RES,
+ REVANCED_SETTINGS_MENU_ITEM_ICON_RES,
+ )
+
+ // Intercept settings menu creation and add new menu item.
+ menuGroupsUpdatedMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ sget-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$REVANCED_SETTINGS_MENU_ITEM_NAME:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR
+ invoke-static { p1, v0 }, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuCreation(Ljava/util/List;Ljava/lang/Object;)Ljava/util/List;
+ move-result-object p1
+ """,
+ )
+
+ // Intercept onclick events for the settings menu
+
+ menuGroupsOnClickMatch.mutableMethod.addInstructionsWithLabels(
+ 0,
+ """
+ invoke-static {p1}, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuOnClick(Ljava/lang/Enum;)Z
+ move-result p2
+ if-eqz p2, :no_rv_settings_onclick
+ sget-object p1, $MENU_DISMISS_EVENT_CLASS_DESCRIPTOR->INSTANCE:$MENU_DISMISS_EVENT_CLASS_DESCRIPTOR
+ invoke-virtual { p0, p1 }, Ltv/twitch/android/core/mvp/viewdelegate/RxViewDelegate;->pushEvent(Ltv/twitch/android/core/mvp/viewdelegate/ViewDelegateEvent;)V
+ return-void
+ """,
+ ExternalLabel(
+ "no_rv_settings_onclick",
+ menuGroupsOnClickMatch.mutableMethod.getInstruction(0),
+ ),
+ )
+ }
+
+ finalize {
+ PreferenceScreen.close()
+ }
+}
+
+/**
+ * Preference screens patches should add their settings to.
+ */
+@Suppress("ktlint:standard:property-naming")
+internal object PreferenceScreen : BasePreferenceScreen() {
+ val ADS = CustomScreen("revanced_ads_screen")
+ val CHAT = CustomScreen("revanced_chat_screen")
+ val MISC = CustomScreen("revanced_misc_screen")
+
+ internal class CustomScreen(key: String) : Screen(key) {
+ /* Categories */
+ val GENERAL = CustomCategory("revanced_general_category")
+ val OTHER = CustomCategory("revanced_other_category")
+ val CLIENT_SIDE = CustomCategory("revanced_client_ads_category")
+ val SURESTREAM = CustomCategory("revanced_surestream_ads_category")
+
+ internal inner class CustomCategory(key: String) : Category(key) {
+ /* For Twitch, we need to load our CustomPreferenceCategory class instead of the default one. */
+ override fun transform(): PreferenceCategory = PreferenceCategory(
+ key,
+ preferences = preferences,
+ tag = "app.revanced.extension.twitch.settings.preference.CustomPreferenceCategory",
+ )
+ }
+ }
+
+ override fun commit(screen: app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference) {
+ preferences += screen
+ }
+}
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
new file mode 100644
index 000000000..dc100acb1
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt
@@ -0,0 +1,27 @@
+package app.revanced.patches.twitter.interaction.downloads
+
+import app.revanced.patcher.fingerprint
+import com.android.tools.smali.dexlib2.AccessFlags
+import com.android.tools.smali.dexlib2.Opcode
+
+internal val buildMediaOptionsSheetFingerprint = fingerprint {
+ opcodes(
+ Opcode.IF_EQ,
+ Opcode.SGET_OBJECT,
+ Opcode.GOTO_16,
+ Opcode.NEW_INSTANCE,
+ )
+ strings("mediaEntity", "media_options_sheet")
+}
+
+internal val constructMediaOptionsSheetFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
+ returns("V")
+ strings("captionsState")
+}
+
+internal val showDownloadVideoUpsellBottomSheetFingerprint = fingerprint {
+ returns("Z")
+ strings("mediaEntity", "url")
+ opcodes(Opcode.IF_EQZ)
+}
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
new file mode 100644
index 000000000..0f9157d6b
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt
@@ -0,0 +1,70 @@
+package app.revanced.patches.twitter.interaction.downloads
+
+import app.revanced.patcher.Match
+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.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
+
+@Suppress("unused")
+val unlockDownloadsPatch = bytecodePatch(
+ name = "Unlock downloads",
+ description = "Unlocks the ability to download any video. GIFs can be downloaded via the menu on long press.",
+) {
+ compatibleWith("com.twitter.android")
+
+ val constructMediaOptionsSheetMatch by constructMediaOptionsSheetFingerprint()
+ val showDownloadVideoUpsellBottomSheetMatch by showDownloadVideoUpsellBottomSheetFingerprint()
+ val buildMediaOptionsSheetMatch by buildMediaOptionsSheetFingerprint()
+
+ fun Match.patch(getRegisterAndIndex: Match.() -> Pair