chore: Merge branch dev to main (#3724)

This commit is contained in:
LisoUseInAIKyrios 2024-10-19 21:40:07 -04:00 committed by GitHub
commit 14af4c07bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
250 changed files with 5410 additions and 3724 deletions

View File

@ -25,7 +25,8 @@ jobs:
pr_body: |
This pull request will ${{ env.MESSAGE }}.
## Dependencies before merge
## Before merging this PR
- [ ] https://github.com/revanced/revanced-integrations
- [ ] Remember about https://github.com/revanced/revanced-integrations
- [ ] Pull translations from Crowdin
pr_draft: true

View File

@ -8,6 +8,9 @@ on:
jobs:
pull:
name: Pull strings
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -30,6 +33,6 @@ jobs:
github_user_name: revanced-bot
github_user_email: github@revanced.app
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@ -24,6 +24,5 @@ jobs:
config: crowdin.yml
upload_sources: true
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@ -10,6 +10,9 @@ on:
jobs:
release:
name: Release
permissions:
contents: write
packages: write
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -46,5 +49,5 @@ jobs:
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm exec semantic-release

View File

@ -21,11 +21,11 @@
"@semantic-release/git",
{
"assets": [
"README.md",
"CHANGELOG.md",
"gradle.properties",
"patches.json"
]
],
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
[

View File

@ -1,3 +1,103 @@
# [4.17.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.12...v4.17.0-dev.13) (2024-10-19)
### Bug Fixes
* **YouTube - GmsCore support:** Add more replacements ([4d39770](https://github.com/ReVanced/revanced-patches/commit/4d39770602b39b6cb399eb0d8c52947b6ebafbb0))
* **YouTube - GmsCore support:** Remove unclear patch changes ([021d858](https://github.com/ReVanced/revanced-patches/commit/021d8584a7f5a6d1a028c5d18dc91a3b988b2884))
### Performance Improvements
* **YouTube - GmsCore support:** Improve performance by using hashsets ([2c5d390](https://github.com/ReVanced/revanced-patches/commit/2c5d390fb1275dc3da5a3b912e221b7d594a1561))
# [4.17.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.11...v4.17.0-dev.12) (2024-10-19)
### Features
* **YouTube - Hide Shorts components:** Hide `Hashtag` button ([#3787](https://github.com/ReVanced/revanced-patches/issues/3787)) ([828a634](https://github.com/ReVanced/revanced-patches/commit/828a634667c4005a90f3e469ad2c5d69387f0760))
# [4.17.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.10...v4.17.0-dev.11) (2024-10-19)
### Features
* **YouTube:** Support versions `19.25` and `19.34` ([#3629](https://github.com/ReVanced/revanced-patches/issues/3629)) ([049e7f0](https://github.com/ReVanced/revanced-patches/commit/049e7f081358d2e1bf87d30e87b01c61b5eeafcc))
# [4.17.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.9...v4.17.0-dev.10) (2024-10-17)
### Features
* **Facebook:** Add `Hide sponsored stories` patch ([#3627](https://github.com/ReVanced/revanced-patches/issues/3627)) ([214c72b](https://github.com/ReVanced/revanced-patches/commit/214c72baeb7f87f21cd2ca34301ab11fa0ff1a4f))
# [4.17.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.8...v4.17.0-dev.9) (2024-10-17)
### Features
* **Sync for Reddit:** Add `Fix video downloads` patch ([#3739](https://github.com/ReVanced/revanced-patches/issues/3739)) ([a47ee38](https://github.com/ReVanced/revanced-patches/commit/a47ee38b1cdd974a959008006ecaf58917addc60))
# [4.17.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.7...v4.17.0-dev.8) (2024-10-17)
### Features
* **Twitter:** Add `Change link sharing domain` patch ([#3753](https://github.com/ReVanced/revanced-patches/issues/3753)) ([9269a07](https://github.com/ReVanced/revanced-patches/commit/9269a076b674ecdcf478bca842238f6e30869f44))
# [4.17.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.6...v4.17.0-dev.7) (2024-10-17)
### Bug Fixes
* **Twitter - Unlock downloads:** Make it work with latest versions ([#3782](https://github.com/ReVanced/revanced-patches/issues/3782)) ([5189122](https://github.com/ReVanced/revanced-patches/commit/5189122006b0f72d5bfb50422021c3b0f3a9ae4a))
* **YouTube - Spoof video streams:** Fix playback for Android VR by removing invalid body as well ([#3769](https://github.com/ReVanced/revanced-patches/issues/3769)) ([5150a15](https://github.com/ReVanced/revanced-patches/commit/5150a15ad4ca73a747f0a89f933db7f2d686ec2d))
# [4.17.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.5...v4.17.0-dev.6) (2024-10-14)
### Features
* **YouTube - Hide Shorts components:** Add option to hide `Use template`, `Upcoming`, `Green screen` buttons ([#3752](https://github.com/ReVanced/revanced-patches/issues/3752)) ([f71c406](https://github.com/ReVanced/revanced-patches/commit/f71c4068bc646d02954b59fac4756f1419c55dbe))
# [4.17.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.4...v4.17.0-dev.5) (2024-10-07)
### Features
* **Backdrops - Pro unlock:** Support latest versions by removing version constraint ([a62b506](https://github.com/ReVanced/revanced-patches/commit/a62b50691c49d1ce529a7c9c4e49da0d0dd46df2))
# [4.17.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.3...v4.17.0-dev.4) (2024-10-06)
### Bug Fixes
* **YouTube - Hide layout components:** Adjust settings text ([#3745](https://github.com/ReVanced/revanced-patches/issues/3745)) ([13998bb](https://github.com/ReVanced/revanced-patches/commit/13998bbf95ac3cde8bf24754d60258d0ff9bc4f4))
# [4.17.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.2...v4.17.0-dev.3) (2024-10-06)
### Features
* **YouTube - Hide layout components:** Add option to hide Yoodles (YouTube Doodles) ([#3743](https://github.com/ReVanced/revanced-patches/issues/3743)) ([b8c8916](https://github.com/ReVanced/revanced-patches/commit/b8c89164cf3911ac3842df9b0d2ec42b52213505))
# [4.17.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.1...v4.17.0-dev.2) (2024-10-05)
### Features
* **Willhaben:** Add `Hide ads` patch ([#3740](https://github.com/ReVanced/revanced-patches/issues/3740)) ([1fe3a52](https://github.com/ReVanced/revanced-patches/commit/1fe3a523e99ccfe556d88800686e34ac6ed77b2c))
# [4.17.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.16.1-dev.1...v4.17.0-dev.1) (2024-10-02)
### Features
* **YouTube - Hide Shorts components:** Add option to hide like fountain ([#3731](https://github.com/ReVanced/revanced-patches/issues/3731)) ([00a99dd](https://github.com/ReVanced/revanced-patches/commit/00a99dd13be6e5c44fa691d74c92b23ce6ba659d))
## [4.16.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.16.0...v4.16.1-dev.1) (2024-10-01)
# [4.16.0](https://github.com/ReVanced/revanced-patches/compare/v4.15.0...v4.16.0) (2024-09-30)

View File

@ -263,6 +263,12 @@ public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatch : ap
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
@ -840,6 +846,12 @@ public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/u
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;
}
@ -1490,6 +1502,12 @@ public final class app/revanced/patches/twitter/misc/hook/patch/recommendation/H
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
@ -1520,6 +1538,12 @@ public final class app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnloc
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
@ -1826,10 +1850,6 @@ public final class app/revanced/patches/youtube/layout/startpage/ChangeStartPage
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/layout/startpage/fingerprints/StartActivityFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startpage/fingerprints/StartActivityFingerprint;
}
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
@ -2178,12 +2198,28 @@ public final class app/revanced/util/BytecodeUtilsKt {
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

View File

@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 4.16.0
version = 4.17.0-dev.13

321
package-lock.json generated
View File

@ -8,8 +8,8 @@
"@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.2",
"semantic-release": "^24.0.0"
"gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.1.2"
}
},
"node_modules/@babel/code-frame": {
@ -235,13 +235,13 @@
"license": "MIT"
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "11.3.3",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.3.tgz",
"integrity": "sha512-o4WRoOJZlKqEEgj+i9CpcmnByvtzoUYC6I8PD2SA95M+BJ2x8h7oLcVOg9qcowWXBOdcTRsMZiwvM3EyLm9AfA==",
"version": "11.3.5",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.5.tgz",
"integrity": "sha512-cgwIRtKrpwhLoBi0CUNuY83DPGRMaWVjqVI/bGKsLJ4PzyWZNaEmhHroI2xlrVXkk6nFv0IsZpOp+ZWSWUS2AQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.5.0"
"@octokit/types": "^13.6.0"
},
"engines": {
"node": ">= 18"
@ -251,9 +251,9 @@
}
},
"node_modules/@octokit/plugin-retry": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.1.tgz",
"integrity": "sha512-G9Ue+x2odcb8E1XIPhaFBnTTIrrUDfXN05iFXiqhR+SeeeDMMILcAnysOsxUpEWcQp2e5Ft397FCXTcPkiPkLw==",
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.2.tgz",
"integrity": "sha512-XOWnPpH2kJ5VTwozsxGurw+svB2e61aWlmk5EVIYZPwFK5F9h4cyPyj9CIKRyMXMHSwpIsI3mPOdpMmrRhe7UQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -302,9 +302,9 @@
}
},
"node_modules/@octokit/request-error": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz",
"integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==",
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz",
"integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -315,9 +315,9 @@
}
},
"node_modules/@octokit/types": {
"version": "13.5.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
"integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
"version": "13.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.0.tgz",
"integrity": "sha512-CrooV/vKCXqwLa+osmHLIMUb87brpgUqlqkPGc6iE2wCkUvTrHiXFMhAKoDDaAAYJrtKtrFTgSQTg5nObBEaew==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -355,9 +355,9 @@
"license": "ISC"
},
"node_modules/@pnpm/npm-conf": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz",
"integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz",
"integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -977,6 +977,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@saithodev/semantic-release-backmerge/node_modules/hosted-git-info": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
"integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
"dev": true,
"license": "ISC",
"dependencies": {
"lru-cache": "^10.0.1"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/@saithodev/semantic-release-backmerge/node_modules/human-signals": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
@ -1372,9 +1385,9 @@
}
},
"node_modules/@semantic-release/github": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-10.1.1.tgz",
"integrity": "sha512-sSmsBKGpAlTtXf9rUJf/si16p+FwPEsvsJRjl3KCwFP0WywaSpynvUhlYvE18n5rzkQNbGJnObAKIoo3xFMSjA==",
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.0.tgz",
"integrity": "sha512-Uon6G6gJD8U1JNvPm7X0j46yxNRJ8Ui6SgK4Zw5Ktu8RgjEft3BGn+l/RX1TTzhhO3/uUcKuqM+/9/ETFxWS/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1399,7 +1412,7 @@
"node": ">=20.8.1"
},
"peerDependencies": {
"semantic-release": ">=20.1.0"
"semantic-release": ">=24.1.0"
}
},
"node_modules/@semantic-release/github/node_modules/@semantic-release/error": {
@ -1569,9 +1582,9 @@
}
},
"node_modules/@semantic-release/npm/node_modules/execa": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz",
"integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==",
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-9.4.0.tgz",
"integrity": "sha512-yKHlle2YGxZE842MERVIplWwNH5VYmqqcPFgtnlU//K8gxuFFXu0pwd/CrfXTumFpeEiufsP7+opT/bPJa1yVw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1579,10 +1592,10 @@
"cross-spawn": "^7.0.3",
"figures": "^6.1.0",
"get-stream": "^9.0.0",
"human-signals": "^7.0.0",
"human-signals": "^8.0.0",
"is-plain-obj": "^4.1.0",
"is-stream": "^4.0.1",
"npm-run-path": "^5.2.0",
"npm-run-path": "^6.0.0",
"pretty-ms": "^9.0.0",
"signal-exit": "^4.1.0",
"strip-final-newline": "^4.0.0",
@ -1613,9 +1626,9 @@
}
},
"node_modules/@semantic-release/npm/node_modules/human-signals": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz",
"integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz",
"integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -1649,16 +1662,17 @@
}
},
"node_modules/@semantic-release/npm/node_modules/npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
"integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^4.0.0"
"path-key": "^4.0.0",
"unicorn-magic": "^0.3.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@ -1703,6 +1717,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@semantic-release/npm/node_modules/unicorn-magic": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@semantic-release/release-notes-generator": {
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.1.tgz",
@ -2273,13 +2300,13 @@
}
},
"node_modules/debug": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.1.2"
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@ -2358,9 +2385,9 @@
"license": "MIT"
},
"node_modules/env-ci": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.0.0.tgz",
"integrity": "sha512-apikxMgkipkgTvMdRT9MNqWx5VLOci79F4VBd7Op/7OPjjoanjdAvn6fglMCCEf/1bAh8eOiuEVCUs4V3qP3nQ==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.0.tgz",
"integrity": "sha512-Z8dnwSDbV1XYM9SBF2J0GcNVvmfmfh3a49qddGIROhBoVro6MZVTji15z/sJbQ2ko2ei8n988EU1wzoLU/tF+g==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2549,9 +2576,9 @@
}
},
"node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
@ -2847,9 +2874,9 @@
"license": "ISC"
},
"node_modules/gradle-semantic-release-plugin": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/gradle-semantic-release-plugin/-/gradle-semantic-release-plugin-1.9.2.tgz",
"integrity": "sha512-8qpf4GYFPQ+UMUymYBy/VchOOwLILAWzZMrZX1R0RR3JMgJBMN2R0tJn92R/3rXmxx4OAqwUFH6Np51eFoxr3w==",
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/gradle-semantic-release-plugin/-/gradle-semantic-release-plugin-1.10.1.tgz",
"integrity": "sha512-Q4dLAFICjPouUyRRHEKK8cXNB75nraXoioYZDZlVQOg4sYKudnTDZ3ohLmV3k4cPGiiMCh1ckXETkx9JnuyKmA==",
"dev": true,
"funding": [
{
@ -2925,16 +2952,16 @@
}
},
"node_modules/hosted-git-info": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
"integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.0.0.tgz",
"integrity": "sha512-4nw3vOVR+vHUOT8+U4giwe2tcGv+R3pwwRidUe67DoMBTjhrfr6rZYJVVwdkBE+Um050SG+X9tf0Jo4fOpn01w==",
"dev": true,
"license": "ISC",
"dependencies": {
"lru-cache": "^10.0.1"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/http-proxy-agent": {
@ -2976,9 +3003,9 @@
}
},
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
"integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"license": "MIT",
"engines": {
@ -3191,9 +3218,9 @@
}
},
"node_modules/is-unicode-supported": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz",
"integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
"integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
"dev": true,
"license": "MIT",
"engines": {
@ -3497,9 +3524,9 @@
}
},
"node_modules/micromatch": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -3547,9 +3574,9 @@
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
@ -3610,6 +3637,19 @@
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/normalize-package-data/node_modules/hosted-git-info": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
"integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
"dev": true,
"license": "ISC",
"dependencies": {
"lru-cache": "^10.0.1"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/normalize-url": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
@ -3624,9 +3664,9 @@
}
},
"node_modules/npm": {
"version": "10.8.2",
"resolved": "https://registry.npmjs.org/npm/-/npm-10.8.2.tgz",
"integrity": "sha512-x/AIjFIKRllrhcb48dqUNAAZl0ig9+qMuN91RpZo3Cb2+zuibfh+KISl6+kVVyktDz230JKc208UkQwwMqyB+w==",
"version": "10.8.3",
"resolved": "https://registry.npmjs.org/npm/-/npm-10.8.3.tgz",
"integrity": "sha512-0IQlyAYvVtQ7uOhDFYZCGK8kkut2nh8cpAdA9E6FvRSJaTgtZRZgNjlC5ZCct//L73ygrpY93CxXpRJDtNqPVg==",
"bundleDependencies": [
"@isaacs/string-locale-compare",
"@npmcli/arborist",
@ -3719,13 +3759,13 @@
"@sigstore/tuf": "^2.3.4",
"abbrev": "^2.0.0",
"archy": "~1.0.0",
"cacache": "^18.0.3",
"cacache": "^18.0.4",
"chalk": "^5.3.0",
"ci-info": "^4.0.0",
"cli-columns": "^4.0.0",
"fastest-levenshtein": "^1.0.16",
"fs-minipass": "^3.0.3",
"glob": "^10.4.2",
"glob": "^10.4.5",
"graceful-fs": "^4.2.11",
"hosted-git-info": "^7.0.2",
"ini": "^4.1.3",
@ -3734,7 +3774,7 @@
"json-parse-even-better-errors": "^3.0.2",
"libnpmaccess": "^8.0.6",
"libnpmdiff": "^6.1.4",
"libnpmexec": "^8.1.3",
"libnpmexec": "^8.1.4",
"libnpmfund": "^5.0.12",
"libnpmhook": "^10.0.5",
"libnpmorg": "^6.0.6",
@ -3748,12 +3788,12 @@
"minipass": "^7.1.1",
"minipass-pipeline": "^1.2.4",
"ms": "^2.1.2",
"node-gyp": "^10.1.0",
"node-gyp": "^10.2.0",
"nopt": "^7.2.1",
"normalize-package-data": "^6.0.2",
"npm-audit-report": "^5.0.0",
"npm-install-checks": "^6.3.0",
"npm-package-arg": "^11.0.2",
"npm-package-arg": "^11.0.3",
"npm-pick-manifest": "^9.1.0",
"npm-profile": "^10.0.0",
"npm-registry-fetch": "^17.1.0",
@ -3764,7 +3804,7 @@
"proc-log": "^4.2.0",
"qrcode-terminal": "^0.12.0",
"read": "^3.0.1",
"semver": "^7.6.2",
"semver": "^7.6.3",
"spdx-expression-parse": "^4.0.0",
"ssri": "^10.0.6",
"supports-color": "^9.4.0",
@ -4335,7 +4375,7 @@
}
},
"node_modules/npm/node_modules/cacache": {
"version": "18.0.3",
"version": "18.0.4",
"dev": true,
"inBundle": true,
"license": "ISC",
@ -4502,7 +4542,7 @@
}
},
"node_modules/npm/node_modules/debug": {
"version": "4.3.5",
"version": "4.3.6",
"dev": true,
"inBundle": true,
"license": "MIT",
@ -4586,7 +4626,7 @@
}
},
"node_modules/npm/node_modules/foreground-child": {
"version": "3.2.1",
"version": "3.3.0",
"dev": true,
"inBundle": true,
"license": "ISC",
@ -4614,7 +4654,7 @@
}
},
"node_modules/npm/node_modules/glob": {
"version": "10.4.2",
"version": "10.4.5",
"dev": true,
"inBundle": true,
"license": "ISC",
@ -4629,9 +4669,6 @@
"bin": {
"glob": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
@ -4815,16 +4852,13 @@
"license": "ISC"
},
"node_modules/npm/node_modules/jackspeak": {
"version": "3.4.0",
"version": "3.4.3",
"dev": true,
"inBundle": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
@ -4910,7 +4944,7 @@
}
},
"node_modules/npm/node_modules/libnpmexec": {
"version": "8.1.3",
"version": "8.1.4",
"dev": true,
"inBundle": true,
"license": "ISC",
@ -5044,13 +5078,10 @@
}
},
"node_modules/npm/node_modules/lru-cache": {
"version": "10.2.2",
"version": "10.4.3",
"dev": true,
"inBundle": true,
"license": "ISC",
"engines": {
"node": "14 || >=16.14"
}
"license": "ISC"
},
"node_modules/npm/node_modules/make-fetch-happen": {
"version": "13.0.1",
@ -5262,7 +5293,7 @@
}
},
"node_modules/npm/node_modules/node-gyp": {
"version": "10.1.0",
"version": "10.2.0",
"dev": true,
"inBundle": true,
"license": "MIT",
@ -5273,9 +5304,9 @@
"graceful-fs": "^4.2.6",
"make-fetch-happen": "^13.0.0",
"nopt": "^7.0.0",
"proc-log": "^3.0.0",
"proc-log": "^4.1.0",
"semver": "^7.3.5",
"tar": "^6.1.2",
"tar": "^6.2.1",
"which": "^4.0.0"
},
"bin": {
@ -5285,15 +5316,6 @@
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/npm/node_modules/node-gyp/node_modules/proc-log": {
"version": "3.0.0",
"dev": true,
"inBundle": true,
"license": "ISC",
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/npm/node_modules/nopt": {
"version": "7.2.1",
"dev": true,
@ -5366,7 +5388,7 @@
}
},
"node_modules/npm/node_modules/npm-package-arg": {
"version": "11.0.2",
"version": "11.0.3",
"dev": true,
"inBundle": true,
"license": "ISC",
@ -5540,7 +5562,7 @@
}
},
"node_modules/npm/node_modules/postcss-selector-parser": {
"version": "6.1.0",
"version": "6.1.2",
"dev": true,
"inBundle": true,
"license": "MIT",
@ -5678,7 +5700,7 @@
"optional": true
},
"node_modules/npm/node_modules/semver": {
"version": "7.6.2",
"version": "7.6.3",
"dev": true,
"inBundle": true,
"license": "ISC",
@ -6437,9 +6459,9 @@
}
},
"node_modules/picocolors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"dev": true,
"license": "ISC"
},
@ -6481,9 +6503,9 @@
}
},
"node_modules/pretty-ms": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz",
"integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==",
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.1.0.tgz",
"integrity": "sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6738,15 +6760,15 @@
"license": "MIT"
},
"node_modules/semantic-release": {
"version": "24.0.0",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.0.0.tgz",
"integrity": "sha512-v46CRPw+9eI3ZuYGF2oAjqPqsfbnfFTwLBgQsv/lch4goD09ytwOTESMN4QIrx/wPLxUGey60/NMx+ANQtWRsA==",
"version": "24.1.2",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.1.2.tgz",
"integrity": "sha512-hvEJ7yI97pzJuLsDZCYzJgmRxF8kiEJvNZhf0oiZQcexw+Ycjy4wbdsn/sVMURgNCu8rwbAXJdBRyIxM4pe32g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@semantic-release/commit-analyzer": "^13.0.0-beta.1",
"@semantic-release/error": "^4.0.0",
"@semantic-release/github": "^10.0.0",
"@semantic-release/github": "^11.0.0",
"@semantic-release/npm": "^12.0.0",
"@semantic-release/release-notes-generator": "^14.0.0-beta.1",
"aggregate-error": "^5.0.0",
@ -6759,7 +6781,7 @@
"get-stream": "^6.0.0",
"git-log-parser": "^1.2.0",
"hook-std": "^3.0.0",
"hosted-git-info": "^7.0.0",
"hosted-git-info": "^8.0.0",
"import-from-esm": "^1.3.1",
"lodash-es": "^4.17.21",
"marked": "^12.0.0",
@ -6851,9 +6873,9 @@
}
},
"node_modules/semantic-release/node_modules/execa": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz",
"integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==",
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-9.4.0.tgz",
"integrity": "sha512-yKHlle2YGxZE842MERVIplWwNH5VYmqqcPFgtnlU//K8gxuFFXu0pwd/CrfXTumFpeEiufsP7+opT/bPJa1yVw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6861,10 +6883,10 @@
"cross-spawn": "^7.0.3",
"figures": "^6.1.0",
"get-stream": "^9.0.0",
"human-signals": "^7.0.0",
"human-signals": "^8.0.0",
"is-plain-obj": "^4.1.0",
"is-stream": "^4.0.1",
"npm-run-path": "^5.2.0",
"npm-run-path": "^6.0.0",
"pretty-ms": "^9.0.0",
"signal-exit": "^4.1.0",
"strip-final-newline": "^4.0.0",
@ -6895,9 +6917,9 @@
}
},
"node_modules/semantic-release/node_modules/human-signals": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz",
"integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz",
"integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -6931,16 +6953,17 @@
}
},
"node_modules/semantic-release/node_modules/npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
"integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^4.0.0"
"path-key": "^4.0.0",
"unicorn-magic": "^0.3.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@ -6998,6 +7021,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/unicorn-magic": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver": {
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
@ -7239,9 +7275,9 @@
}
},
"node_modules/spdx-license-ids": {
"version": "3.0.18",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz",
"integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==",
"version": "3.0.20",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz",
"integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==",
"dev": true,
"license": "CC0-1.0"
},
@ -7365,9 +7401,9 @@
}
},
"node_modules/supports-hyperlinks": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz",
"integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz",
"integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -7376,6 +7412,9 @@
},
"engines": {
"node": ">=14.18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/temp-dir": {
@ -7530,9 +7569,9 @@
}
},
"node_modules/type-fest": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.22.1.tgz",
"integrity": "sha512-9tHNEa0Ov81YOopiVkcCJVz5TM6AEQ+CHHjFIktqPnE3NV0AHIkx+gh9tiCl58m/66wWxkOC9eltpa75J4lQPA==",
"version": "4.26.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz",
"integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
@ -7543,9 +7582,9 @@
}
},
"node_modules/uglify-js": {
"version": "3.19.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz",
"integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==",
"version": "3.19.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
"integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
"dev": true,
"license": "BSD-2-Clause",
"optional": true,

View File

@ -3,7 +3,7 @@
"@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.2",
"semantic-release": "^24.0.0"
"gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.1.2"
}
}

View File

@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Pro unlock",
compatiblePackages = [CompatiblePackage("com.backdrops.wallpapers", ["4.52"])]
compatiblePackages = [CompatiblePackage("com.backdrops.wallpapers")]
)
@Suppress("unused")
object ProUnlockPatch : BytecodePatch(
@ -34,4 +34,4 @@ object ProUnlockPatch : BytecodePatch(
} ?: throw ProUnlockFingerprint.exception
}
}
}

View File

@ -0,0 +1,96 @@
package app.revanced.patches.facebook.ads.mainfeed
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.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.BaseModelMapperFingerprint
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GetSponsoredDataModelTemplateFingerprint
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GetStoryVisibilityFingerprint
import app.revanced.util.exception
import app.revanced.util.resultOrThrow
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
@Patch(
name = "Hide 'Sponsored Stories'",
compatiblePackages = [CompatiblePackage("com.facebook.katana")],
)
@Suppress("unused")
object HideSponsoredStoriesPatch : BytecodePatch(
setOf(GetStoryVisibilityFingerprint, GetSponsoredDataModelTemplateFingerprint, BaseModelMapperFingerprint),
) {
private const val GRAPHQL_STORY_TYPE = "Lcom/facebook/graphql/model/GraphQLStory;"
override fun execute(context: BytecodeContext) {
GetStoryVisibilityFingerprint.result?.apply {
val sponsoredDataModelTemplateMethod = GetSponsoredDataModelTemplateFingerprint.resultOrThrow().method
val baseModelMapperMethod = BaseModelMapperFingerprint.resultOrThrow().method
val baseModelWithTreeType = baseModelMapperMethod.returnType
// 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(
classDef.type,
"getSponsoredData",
listOf(ImmutableMethodParameter(GRAPHQL_STORY_TYPE, null, null)),
baseModelWithTreeType,
AccessFlags.PRIVATE or AccessFlags.STATIC,
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<Instruction31i>()
.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
""",
)
}
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.
mutableMethod.addInstructionsWithLabels(
scanResult.patternScanResult!!.startIndex,
"""
instance-of v0, p0, $GRAPHQL_STORY_TYPE
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
""",
)
} ?: throw GetStoryVisibilityFingerprint.exception
}
}

View File

@ -0,0 +1,21 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
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
internal object BaseModelMapperFingerprint : MethodFingerprint(
accessFlags = (AccessFlags.PUBLIC or AccessFlags.FINAL),
parameters = listOf("Ljava/lang/Class","I","I"),
returnType = "Lcom/facebook/graphql/modelutil/BaseModelWithTree;",
opcodes = listOf(
Opcode.SGET_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.IF_EQ
)
)

View File

@ -0,0 +1,23 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
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
internal object GetSponsoredDataModelTemplateFingerprint : MethodFingerprint(
accessFlags = (AccessFlags.PUBLIC or AccessFlags.FINAL),
parameters = listOf(),
returnType = "L",
opcodes = listOf(
Opcode.CONST,
Opcode.CONST,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT
),
customFingerprint = { methodDef, classDef ->
classDef.type == "Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;"
}
)

View File

@ -0,0 +1,23 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
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.Annotation
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
internal object GetStoryVisibilityFingerprint : MethodFingerprint(
returnType = "Ljava/lang/String;",
accessFlags = (AccessFlags.PUBLIC or AccessFlags.STATIC),
opcodes = listOf(
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.CONST
),
strings = listOf("This should not be called for base class object"),
)

View File

@ -3,7 +3,6 @@ package app.revanced.patches.music.misc.gms
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.music.misc.gms.fingerprints.CastDynamiteModuleV2Fingerprint
import app.revanced.patches.music.misc.gms.fingerprints.MusicActivityOnCreateFingerprint
import app.revanced.patches.music.misc.gms.fingerprints.PrimeMethodFingerprint
import app.revanced.patches.music.misc.integrations.IntegrationsPatch
@ -16,7 +15,6 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
toPackageName = REVANCED_MUSIC_PACKAGE_NAME,
primeMethodFingerprint = PrimeMethodFingerprint,
earlyReturnFingerprints = setOf(
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
),
mainActivityOnCreateFingerprint = MusicActivityOnCreateFingerprint,
@ -26,7 +24,6 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
CompatiblePackage("com.google.android.apps.youtube.music"),
),
fingerprints = setOf(
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeMethodFingerprint,
),

View File

@ -1,8 +0,0 @@
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object CastDynamiteModuleV2Fingerprint : MethodFingerprint(
strings = listOf("Failed to load module via V2: ")
)

View File

@ -0,0 +1,62 @@
package app.revanced.patches.reddit.customclients.syncforreddit.fix.video
import app.revanced.patcher.data.BytecodeContext
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.patches.reddit.customclients.syncforreddit.fix.video.fingerprints.ParseRedditVideoNetworkResponseFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
@Patch(
name = "Fix video downloads",
description = "Fixes a bug in Sync's MPD parser resulting in only the audio-track being saved.",
compatiblePackages = [
CompatiblePackage("com.laurencedawson.reddit_sync"),
CompatiblePackage("com.laurencedawson.reddit_sync.pro"),
CompatiblePackage("com.laurencedawson.reddit_sync.dev"),
],
requiresIntegrations = true,
)
@Suppress("unused")
object FixVideoDownloadsPatch : BytecodePatch(
fingerprints = setOf(ParseRedditVideoNetworkResponseFingerprint),
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch;"
private const val GET_LINKS_METHOD = "getLinks([B)[Ljava/lang/String;"
override fun execute(context: BytecodeContext) {
ParseRedditVideoNetworkResponseFingerprint.resultOrThrow().let {
val scanResult = it.scanResult.patternScanResult!!
val newInstanceIndex = scanResult.startIndex
val invokeDirectIndex = scanResult.endIndex - 1
val buildResponseInstruction = it.mutableMethod.getInstruction<Instruction35c>(invokeDirectIndex)
it.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 }, $INTEGRATIONS_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.
""",
)
}
}
}

View File

@ -0,0 +1,16 @@
package app.revanced.patches.reddit.customclients.syncforreddit.fix.video.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object ParseRedditVideoNetworkResponseFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.NEW_INSTANCE,
Opcode.IGET_OBJECT,
Opcode.INVOKE_DIRECT,
Opcode.CONST_WIDE_32
),
customFingerprint = { methodDef, classDef ->
classDef.sourceFile == "RedditVideoRequest.java" && methodDef.name == "parseNetworkResponse"
}
)

View File

@ -11,7 +11,6 @@ import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.ACTIONS
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.AUTHORITIES
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.PERMISSIONS
import app.revanced.patches.shared.misc.gms.fingerprints.CastDynamiteModuleFingerprint
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME
import app.revanced.patches.shared.misc.gms.fingerprints.GooglePlayUtilityFingerprint
@ -69,7 +68,6 @@ abstract class BaseGmsCoreSupportPatch(
GmsCoreSupportFingerprint,
GooglePlayUtilityFingerprint,
ServiceCheckFingerprint,
CastDynamiteModuleFingerprint,
mainActivityOnCreateFingerprint,
) + fingerprints,
requiresIntegrations = true,
@ -104,10 +102,7 @@ abstract class BaseGmsCoreSupportPatch(
// Return these methods early to prevent the app from crashing.
earlyReturnFingerprints.returnEarly()
ServiceCheckFingerprint.returnEarly()
// Not all apps have CastDynamiteModule, so we need to check if it's present.
if (CastDynamiteModuleFingerprint.result != null) {
CastDynamiteModuleFingerprint.returnEarly()
}
// Google Play Utility is not present in all apps, so we need to check if it's present.
if (GooglePlayUtilityFingerprint.result != null) {
GooglePlayUtilityFingerprint.returnEarly()
@ -236,131 +231,268 @@ abstract class BaseGmsCoreSupportPatch(
/**
* A collection of permissions, intents and content provider authorities
* that are present in GmsCore which need to be transformed.
*
* NOTE: The following were present, but it seems like they are not needed to be transformed:
* - com.google.android.gms.chimera.GmsIntentOperationService
* - com.google.android.gms.phenotype.internal.IPhenotypeCallbacks
* - com.google.android.gms.phenotype.internal.IPhenotypeService
* - com.google.android.gms.phenotype.PACKAGE_NAME
* - com.google.android.gms.phenotype.UPDATE
* - com.google.android.gms.phenotype
*/
private object Constants {
/**
* A list of all permissions.
* All permissions.
*/
val PERMISSIONS = listOf(
// C2DM / GCM
val PERMISSIONS = setOf(
"com.google.android.c2dm.permission.RECEIVE",
"com.google.android.c2dm.permission.SEND",
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
// GAuth
"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 = listOf(
// location
"com.google.android.gms.location.places.ui.PICK_PLACE",
"com.google.android.gms.location.places.GeoDataApi",
"com.google.android.gms.location.places.PlacesApi",
"com.google.android.gms.location.places.PlaceDetectionApi",
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
// C2DM / GCM
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.c2dm.intent.RECEIVE",
"com.google.iid.TOKEN_REQUEST",
"com.google.android.contextmanager.service.ContextManagerService.START",
"com.google.android.gcm.intent.SEND",
// car
"com.google.android.gms.car.service.START",
// people
"com.google.android.gms.people.service.START",
// wearable
"com.google.android.gms.wearable.BIND",
// auth
"com.google.android.gsf.login",
"com.google.android.gsf.action.GET_GLS",
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
"com.google.android.gms.auth.login.LOGIN",
"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.service.START",
"com.google.firebase.auth.api.gms.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",
// fido
"com.google.android.gms.fido.fido2.privileged.START",
// gass
"com.google.android.gms.gass.START",
// games
"com.google.android.gms.games.service.START",
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
// chimera
"com.google.android.gms.chimera",
// fonts
"com.google.android.gms.fonts",
// phenotype
"com.google.android.gms.phenotype.service.START",
// location
"com.google.android.gms.location.reporting.service.START",
// misc
"com.google.android.gms.gmscompliance.service.START",
"com.google.android.gms.oss.licenses.service.START",
"com.google.android.gms.tapandpay.service.BIND",
"com.google.android.gms.measurement.START",
"com.google.android.gms.languageprofile.service.START",
"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.icing.LIGHTWEIGHT_INDEX_SERVICE",
"com.google.android.gms.accountsettings.action.VIEW_SETTINGS",
// potoken
"com.google.android.gms.potokens.service.START",
// droidguard/ safetynet
"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 = listOf(
// gsf
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",
// auth
"com.google.android.gms.auth.accounts",
// chimera
"com.google.android.gms.chimera",
// fonts
"com.google.android.gms.fonts",
// phenotype
"com.google.android.gms.phenotype",
)
}

View File

@ -1,8 +0,0 @@
package app.revanced.patches.shared.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object CastDynamiteModuleFingerprint : MethodFingerprint(
strings = listOf("com.google.android.gms.cast.framework.internal.CastDynamiteModuleImpl")
)

View File

@ -13,7 +13,7 @@ import app.revanced.patches.tiktok.shared.fingerprints.OnRenderFirstFrameFingerp
import app.revanced.util.exception
import app.revanced.util.indexOfFirstInstructionOrThrow
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.TwoRegisterInstruction
@Patch(
name = "Remember clear display",
@ -34,8 +34,8 @@ object RememberClearDisplayPatch : BytecodePatch(
OnClearDisplayEventFingerprint.result?.mutableMethod?.let {
// region Hook the "Clear display" configuration save event to remember the state of clear display.
val isEnabledIndex = it.indexOfFirstInstructionOrThrow { opcode == Opcode.IGET_BOOLEAN } + 1
val isEnabledRegister = it.getInstruction<Instruction22c>(isEnabledIndex - 1).registerA
val isEnabledIndex = it.indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1
val isEnabledRegister = it.getInstruction<TwoRegisterInstruction>(isEnabledIndex - 1).registerA
it.addInstructions(
isEnabledIndex,

View File

@ -10,5 +10,5 @@ internal object BuildMediaOptionsSheetFingerprint : MethodFingerprint(
Opcode.GOTO_16,
Opcode.NEW_INSTANCE,
),
strings = listOf("resources.getString(R.string.post_video)"),
strings = listOf("mediaEntity", "media_options_sheet"),
)

View File

@ -5,6 +5,6 @@ import com.android.tools.smali.dexlib2.Opcode
internal object ShowDownloadVideoUpsellBottomSheetFingerprint : MethodFingerprint(
returnType = "Z",
strings = listOf("variantToDownload.url"),
strings = listOf("mediaEntity", "url"),
opcodes = listOf(Opcode.IF_EQZ)
)

View File

@ -0,0 +1,93 @@
package app.revanced.patches.twitter.misc.links
import app.revanced.patcher.data.BytecodeContext
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 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.patches.twitter.misc.links.fingerprints.LinkBuilderFingerprint
import app.revanced.patches.twitter.misc.links.fingerprints.LinkResourceGetterFingerprint
import app.revanced.patches.twitter.misc.links.fingerprints.LinkSharingDomainFingerprint
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.StringReference
@Patch(
name = "Change link sharing domain",
description = "Replaces the domain name of Twitter links when sharing them.",
compatiblePackages = [CompatiblePackage("com.twitter.android")],
)
@Suppress("unused")
object ChangeLinkSharingDomainPatch : BytecodePatch(
setOf(
LinkBuilderFingerprint,
LinkResourceGetterFingerprint,
LinkSharingDomainFingerprint,
),
) {
private var domainName by stringPatchOption(
key = "domainName",
default = "fxtwitter.com",
title = "Domain name",
description = "The domain name to use when sharing links.",
required = true,
)
// This method is used to build the link that is shared when the "Share via..." button is pressed.
private const val FORMAT_METHOD_RESOURCE_REFERENCE =
"Lapp/revanced/integrations/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
"formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;"
// This method is used to build the link that is shared when the "Copy link" button is pressed.
private const val FORMAT_METHOD_REFERENCE =
"Lapp/revanced/integrations/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
"formatLink(JLjava/lang/String;)Ljava/lang/String;"
override fun execute(context: BytecodeContext) {
LinkSharingDomainFingerprint.result?.let {
val replacementIndex = it.scanResult.stringsScanResult!!.matches.first().index
val domainRegister = it.mutableMethod.getInstruction<OneRegisterInstruction>(replacementIndex).registerA
it.mutableMethod.replaceInstruction(
replacementIndex,
"const-string v$domainRegister, \"https://$domainName\"",
)
} ?: throw LinkSharingDomainFingerprint.exception
// Replace the domain name when copying a link with "Copy link" button.
LinkBuilderFingerprint.result?.let {
it.mutableMethod.apply {
addInstructions(
0,
"""
invoke-static { p0, p1, p2 }, $FORMAT_METHOD_REFERENCE
move-result-object p0
return-object p0
""",
)
}
} ?: throw LinkBuilderFingerprint.exception
// Used in the Share via... dialog.
LinkResourceGetterFingerprint.result?.mutableMethod?.apply {
val constWithParameterName = indexOfFirstInstructionOrThrow {
getReference<StringReference>()?.string?.contains("id.toString()") == true
}
// Format the link with the new domain name register (2 instructions above the const-string).
val formatLinkCallIndex = constWithParameterName - 2
val formatLinkCall = getInstruction<Instruction35c>(formatLinkCallIndex)
// Replace the original method call with the new method call.
replaceInstruction(
formatLinkCallIndex,
"invoke-static { v${formatLinkCall.registerE} }, $FORMAT_METHOD_RESOURCE_REFERENCE",
)
} ?: throw LinkResourceGetterFingerprint.exception
}
}

View File

@ -0,0 +1,8 @@
package app.revanced.patches.twitter.misc.links.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
// Returns a shareable link string based on a tweet ID and a username.
internal object LinkBuilderFingerprint : MethodFingerprint(
strings = listOf("/%1\$s/status/%2\$d"),
)

View File

@ -0,0 +1,12 @@
package app.revanced.patches.twitter.misc.links.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
// Gets Resource string for share link view available by pressing "Share via" button.
internal object LinkResourceGetterFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/content/res/Resources;"),
strings = listOf("res.getString(R.string.t…lUsername, id.toString())"),
)

View File

@ -0,0 +1,7 @@
package app.revanced.patches.twitter.misc.links.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object LinkSharingDomainFingerprint : MethodFingerprint(
strings = listOf("https://fxtwitter.com"),
)

View File

@ -0,0 +1,28 @@
package app.revanced.patches.willhaben.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.willhaben.ads.fingerprints.AdResolverFingerprint
import app.revanced.patches.willhaben.ads.fingerprints.WHAdViewInjectorFingerprint
import app.revanced.util.returnEarly
@Patch(
name = "Hide ads",
description = "Hides all in-app ads.",
compatiblePackages = [CompatiblePackage("at.willhaben")]
)
@Suppress("unused")
object HideAdsPatch : BytecodePatch(
fingerprints = setOf(
AdResolverFingerprint,
WHAdViewInjectorFingerprint
)
) {
override fun execute(context: BytecodeContext) {
arrayOf(AdResolverFingerprint, WHAdViewInjectorFingerprint).forEach {
it.returnEarly()
}
}
}

View File

@ -0,0 +1,17 @@
package app.revanced.patches.willhaben.ads.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object AdResolverFingerprint : MethodFingerprint(
"L",
parameters = listOf("L", "L"),
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
strings = listOf(
"Google Ad is invalid ",
"Google Native Ad is invalid ",
"Criteo Ad is invalid ",
"Amazon Ad is invalid "
)
)

View File

@ -0,0 +1,15 @@
package app.revanced.patches.willhaben.ads.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object WHAdViewInjectorFingerprint : MethodFingerprint(
"V",
parameters = listOf("L", "L", "L", "Z"),
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
strings = listOf("successfulAdView"),
customFingerprint = { _, classDef ->
classDef.type == "Lat/willhaben/advertising/WHAdView;"
}
)

View File

@ -26,30 +26,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -20,30 +20,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -25,30 +25,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -19,24 +19,11 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -25,24 +25,11 @@ import app.revanced.util.resultOrThrow
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -24,30 +24,11 @@ import app.revanced.util.alsoResolve
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -27,27 +27,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
// 18.38.44 patches but crashes on startup.
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -4,46 +4,37 @@ import app.revanced.patcher.data.BytecodeContext
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.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DoubleSpeedSeekNoticeFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DisableFastForwardLegacyFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DisableFastForwardGestureFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DisableFastForwardNoticeFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.SlideToSeekFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
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.MethodReference
@Patch(
name = "Enable slide to seek",
description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player. Including this patch may cause issues with tapping or double tapping the video player overlay.",
description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player. Including this patch may cause issues with the video player overlay, such as missing buttons and ignored taps and double taps.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
],
@ -53,10 +44,13 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
object EnableSlideToSeekPatch : BytecodePatch(
setOf(
SlideToSeekFingerprint,
DoubleSpeedSeekNoticeFingerprint
DisableFastForwardLegacyFingerprint,
DisableFastForwardGestureFingerprint,
DisableFastForwardNoticeFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/SlideToSeekPatch;"
private const val INTEGRATIONS_METHOD_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/SlideToSeekPatch;->isSlideToSeekDisabled(Z)Z"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@ -65,27 +59,74 @@ object EnableSlideToSeekPatch : BytecodePatch(
SwitchPreference("revanced_slide_to_seek")
)
arrayOf(
// Restore the behaviour to slide to seek.
SlideToSeekFingerprint,
// Disable the double speed seek notice.
DoubleSpeedSeekNoticeFingerprint
).map {
it.result ?: throw it.exception
}.forEach {
val insertIndex = it.scanResult.patternScanResult!!.endIndex + 1
var modifiedMethods = false
it.mutableMethod.apply {
val isEnabledRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// Restore the behaviour to slide to seek.
SlideToSeekFingerprint.resultOrThrow().let {
val checkIndex = it.scanResult.patternScanResult!!.startIndex
val checkReference = it.mutableMethod
.getInstruction(checkIndex).getReference<MethodReference>()!!
addInstructions(
insertIndex,
"""
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isSlideToSeekDisabled()Z
move-result v$isEnabledRegister
"""
)
// A/B check method was only called on this class.
it.mutableClass.methods.forEach { method ->
method.implementation!!.instructions.forEachIndexed { index, instruction ->
if (instruction.opcode == Opcode.INVOKE_VIRTUAL &&
instruction.getReference<MethodReference>() == checkReference
) {
method.apply {
val targetRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
addInstructions(
index + 2,
"""
invoke-static { v$targetRegister }, $INTEGRATIONS_METHOD_DESCRIPTOR
move-result v$targetRegister
"""
)
}
modifiedMethods = true
}
}
}
}
if (!modifiedMethods) throw PatchException("Could not find methods to modify")
// Disable the double speed seek gesture.
if (!VersionCheckPatch.is_19_17_or_greater) {
DisableFastForwardLegacyFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.endIndex + 1
val targetRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex,
"""
invoke-static { v$targetRegister }, $INTEGRATIONS_METHOD_DESCRIPTOR
move-result v$targetRegister
"""
)
}
}
} else {
arrayOf(
DisableFastForwardGestureFingerprint,
DisableFastForwardNoticeFingerprint
).forEach { it.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = it.scanResult.patternScanResult!!.endIndex
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static { v$targetRegister }, $INTEGRATIONS_METHOD_DESCRIPTOR
move-result v$targetRegister
"""
)
}
}}
}
}
}

View File

@ -0,0 +1,21 @@
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
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
internal object DisableFastForwardGestureFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = emptyList(),
opcodes = listOf(
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT
),
customFingerprint = { methodDef, classDef ->
methodDef.implementation!!.instructions.count() > 30 &&
classDef.type.endsWith("/NextGenWatchLayout;")
}
)

View File

@ -3,7 +3,7 @@ package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object DoubleSpeedSeekNoticeFingerprint : LiteralValueFingerprint(
internal object DisableFastForwardLegacyFingerprint : LiteralValueFingerprint(
returnType = "Z",
parameters = emptyList(),
opcodes = listOf(Opcode.MOVE_RESULT),

View File

@ -0,0 +1,19 @@
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
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
internal object DisableFastForwardNoticeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = emptyList(),
opcodes = listOf(
Opcode.CHECK_CAST,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT
),
strings = listOf("Failed to easy seek haptics vibrate")
)

View File

@ -2,10 +2,9 @@ package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.containsWideLiteralInstructionValue
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
internal object SeekbarTappingFingerprint : MethodFingerprint(
returnType = "Z",
@ -21,14 +20,6 @@ internal object SeekbarTappingFingerprint : MethodFingerprint(
customFingerprint = custom@{ methodDef, _ ->
if (methodDef.name != "onTouchEvent") return@custom false
methodDef.implementation!!.instructions.any { instruction ->
if (instruction.opcode != Opcode.CONST) return@any false
val literal = (instruction as NarrowLiteralInstruction).narrowLiteral
// onTouchEvent method contains a CONST instruction
// with this literal making it unique with the rest of the properties of this fingerprint.
literal == Integer.MAX_VALUE
}
methodDef.containsWideLiteralInstructionValue(Integer.MAX_VALUE.toLong())
}
)

View File

@ -1,11 +1,19 @@
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object SlideToSeekFingerprint : LiteralValueFingerprint(
returnType = "Z",
parameters = emptyList(),
opcodes = listOf(Opcode.MOVE_RESULT),
literalSupplier = { 45411329 }
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("Landroid/view/View;", "F"),
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.GOTO_16
),
literalSupplier = { 67108864 }
)

View File

@ -1,11 +1,8 @@
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object SwipingUpGestureParentFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf(),
literalSupplier = { 45379021 }

View File

@ -26,30 +26,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -24,30 +24,11 @@ import app.revanced.util.exception
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
],

View File

@ -23,30 +23,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -34,30 +34,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -24,30 +24,11 @@ import com.android.tools.smali.dexlib2.Opcode
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -37,30 +37,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -1,20 +1,23 @@
package app.revanced.patches.youtube.layout.buttons.player.hide
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.buttons.player.hide.HidePlayerButtonsPatch.ParameterOffsets.HAS_NEXT
import app.revanced.patches.youtube.layout.buttons.player.hide.HidePlayerButtonsPatch.ParameterOffsets.HAS_PREVIOUS
import app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints.PlayerControlsVisibilityModelFingerprint
import app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints.PlayerControlsPreviousNextOverlayTouchFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Hide player buttons",
@ -22,43 +25,25 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class
AddResourcesPatch::class,
HidePlayerButtonsResourcePatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
)
@Suppress("unused")
object HidePlayerButtonsPatch : BytecodePatch(
setOf(PlayerControlsVisibilityModelFingerprint)
setOf(PlayerControlsPreviousNextOverlayTouchFingerprint)
) {
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@ -67,29 +52,23 @@ object HidePlayerButtonsPatch : BytecodePatch(
SwitchPreference("revanced_hide_player_buttons")
)
PlayerControlsVisibilityModelFingerprint.result?.apply {
val callIndex = scanResult.patternScanResult!!.endIndex
val callInstruction = mutableMethod.getInstruction<Instruction3rc>(callIndex)
// overriding this parameter register hides the previous and next buttons
val hasNextParameterRegister = callInstruction.startRegister + HAS_NEXT
val hasPreviousParameterRegister = callInstruction.startRegister + HAS_PREVIOUS
mutableMethod.addInstructions(
callIndex,
"""
invoke-static { v$hasNextParameterRegister }, Lapp/revanced/integrations/youtube/patches/HidePlayerButtonsPatch;->previousOrNextButtonIsVisible(Z)Z
move-result v$hasNextParameterRegister
invoke-static { v$hasPreviousParameterRegister }, Lapp/revanced/integrations/youtube/patches/HidePlayerButtonsPatch;->previousOrNextButtonIsVisible(Z)Z
move-result v$hasPreviousParameterRegister
"""
PlayerControlsPreviousNextOverlayTouchFingerprint.resultOrThrow().mutableMethod.apply {
val resourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(
HidePlayerButtonsResourcePatch.playerControlPreviousButtonTouchArea
)
} ?: throw PlayerControlsVisibilityModelFingerprint.exception
}
private object ParameterOffsets {
const val HAS_NEXT = 5
const val HAS_PREVIOUS = 6
val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
opcode == Opcode.INVOKE_STATIC
&& getReference<MethodReference>()?.parameterTypes?.firstOrNull() == "Landroid/view/View;"
}
val viewRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, Lapp/revanced/integrations/youtube/patches/HidePlayerButtonsPatch;" +
"->hidePreviousNextButtons(Landroid/view/View;)V"
)
}
}
}

View File

@ -0,0 +1,24 @@
package app.revanced.patches.youtube.layout.buttons.player.hide
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
@Patch(dependencies = [ResourceMappingPatch::class])
internal object HidePlayerButtonsResourcePatch : ResourcePatch() {
var playerControlPreviousButtonTouchArea = -1L
var playerControlNextButtonTouchArea = -1L
override fun execute(context: ResourceContext) {
playerControlPreviousButtonTouchArea = ResourceMappingPatch[
"id",
"player_control_previous_button_touch_area"
]
playerControlNextButtonTouchArea = ResourceMappingPatch[
"id",
"player_control_next_button_touch_area"
]
}
}

View File

@ -0,0 +1,20 @@
package app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.buttons.player.hide.HidePlayerButtonsResourcePatch
import app.revanced.util.containsWideLiteralInstructionValue
import com.android.tools.smali.dexlib2.AccessFlags
internal object PlayerControlsPreviousNextOverlayTouchFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
strings = listOf("1.0x"),
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionValue(
HidePlayerButtonsResourcePatch.playerControlPreviousButtonTouchArea
) && methodDef.containsWideLiteralInstructionValue(
HidePlayerButtonsResourcePatch.playerControlNextButtonTouchArea
)
}
)

View File

@ -1,9 +0,0 @@
package app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object PlayerControlsVisibilityModelFingerprint : MethodFingerprint(
opcodes = listOf(Opcode.INVOKE_DIRECT_RANGE),
strings = listOf("Missing required properties:", "hasNext", "hasPrevious")
)

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -22,30 +22,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -25,30 +25,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -18,30 +18,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -19,29 +19,11 @@ import app.revanced.util.exception
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -18,13 +18,19 @@ import app.revanced.patches.youtube.layout.hide.general.fingerprints.HideShowMor
import app.revanced.patches.youtube.layout.hide.general.fingerprints.ParseElementFromBufferFingerprint
import app.revanced.patches.youtube.layout.hide.general.fingerprints.PlayerOverlayFingerprint
import app.revanced.patches.youtube.layout.hide.general.fingerprints.ShowWatermarkFingerprint
import app.revanced.patches.youtube.layout.hide.general.fingerprints.YoodlesImageViewFingerprint
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.findOpcodeIndicesReversed
import app.revanced.util.getReference
import app.revanced.util.alsoResolve
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Hide layout components",
@ -40,37 +46,23 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
)
@Suppress("unused")
object HideLayoutComponentsPatch : BytecodePatch(
setOf(ParseElementFromBufferFingerprint, PlayerOverlayFingerprint, HideShowMoreButtonFingerprint),
setOf(
ParseElementFromBufferFingerprint,
PlayerOverlayFingerprint,
HideShowMoreButtonFingerprint,
YoodlesImageViewFingerprint,
),
) {
private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/components/LayoutComponentsFilter;"
@ -128,6 +120,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
SwitchPreference("revanced_hide_search_result_recommendations"),
SwitchPreference("revanced_hide_search_result_shelf_header"),
SwitchPreference("revanced_hide_show_more_button"),
SwitchPreference("revanced_hide_doodles"),
PreferenceScreen(
key = "revanced_hide_keyword_content_screen",
sorting = Sorting.UNSORTED,
@ -191,9 +184,10 @@ object HideLayoutComponentsPatch : BytecodePatch(
// region Watermark (legacy code for old versions of YouTube)
ShowWatermarkFingerprint.also {
it.resolve(context, PlayerOverlayFingerprint.resultOrThrow().classDef)
}.resultOrThrow().mutableMethod.apply {
ShowWatermarkFingerprint.alsoResolve(
context,
PlayerOverlayFingerprint
).mutableMethod.apply {
val index = implementation!!.instructions.size - 5
removeInstruction(index)
@ -226,5 +220,28 @@ object HideLayoutComponentsPatch : BytecodePatch(
}
// endregion
// region 'Yoodles'
YoodlesImageViewFingerprint.resultOrThrow().mutableMethod.apply {
findOpcodeIndicesReversed{
opcode == Opcode.INVOKE_VIRTUAL
&& getReference<MethodReference>()?.name == "setImageDrawable"
}.forEach { insertIndex ->
val register = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
addInstructionsWithLabels(
insertIndex,
"""
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideYoodles(Landroid/graphics/drawable/Drawable;)Landroid/graphics/drawable/Drawable;
move-result-object v$register
if-eqz v$register, :hide
""",
ExternalLabel("hide", getInstruction(insertIndex + 1)),
)
}
}
// endregion
}
}

View File

@ -17,10 +17,17 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
internal object HideLayoutComponentsResourcePatch : ResourcePatch() {
internal var expandButtonDownId: Long = -1
var youTubeLogo = -1L
override fun execute(context: ResourceContext) {
expandButtonDownId = ResourceMappingPatch[
"layout",
"expand_button_down",
]
youTubeLogo = ResourceMappingPatch[
"id",
"youtube_logo"
]
}
}

View File

@ -0,0 +1,13 @@
package app.revanced.patches.youtube.layout.hide.general.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object YoodlesImageViewFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "L"),
returnType = "Landroid/view/View;",
literalSupplier = { HideLayoutComponentsResourcePatch.youTubeLogo }
)

View File

@ -13,6 +13,7 @@ import app.revanced.patches.youtube.layout.hide.infocards.fingerprints.Infocards
import app.revanced.patches.youtube.layout.hide.infocards.fingerprints.InfocardsMethodCallFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.util.alsoResolve
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
@ -29,30 +30,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
@ -68,9 +50,7 @@ object HideInfoCardsPatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/patches/components/HideInfoCardsFilterPatch;"
override fun execute(context: BytecodeContext) {
InfocardsIncognitoFingerprint.also {
it.resolve(context, InfocardsIncognitoParentFingerprint.result!!.classDef)
}.result!!.mutableMethod.apply {
InfocardsIncognitoFingerprint.alsoResolve(context, InfocardsIncognitoParentFingerprint).mutableMethod.apply {
val invokeInstructionIndex = implementation!!.instructions.indexOfFirst {
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal &&
((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V")

View File

@ -23,30 +23,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -23,27 +23,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
// 18.43 is the earliest target this patch works.
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.shared.fingerprints.SeekbarFingerprint
import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
import app.revanced.util.alsoResolve
@Patch(
name = "Hide seekbar",
@ -25,30 +26,11 @@ import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
@ -65,9 +47,10 @@ object HideSeekbarPatch : BytecodePatch(
SwitchPreference("revanced_hide_seekbar_thumbnail")
)
SeekbarFingerprint.result!!.let {
SeekbarOnDrawFingerprint.apply { resolve(context, it.mutableClass) }
}.result!!.mutableMethod.addInstructionsWithLabels(
SeekbarOnDrawFingerprint.alsoResolve(
context,
SeekbarFingerprint
).mutableMethod.addInstructionsWithLabels(
0,
"""
const/4 v0, 0x0

View File

@ -2,6 +2,7 @@ package app.revanced.patches.youtube.layout.hide.shorts
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
@ -13,10 +14,15 @@ import app.revanced.patches.youtube.layout.hide.shorts.fingerprints.*
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.util.exception
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.util.forEachLiteralValueInstruction
import app.revanced.util.alsoResolve
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
import app.revanced.util.indexOfIdResourceOrThrow
import app.revanced.util.injectHideViewCall
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -32,35 +38,17 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
HideShortsComponentsResourcePatch::class,
ResourceMappingPatch::class,
NavigationBarHookPatch::class,
VersionCheckPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
@ -70,7 +58,8 @@ object HideShortsComponentsPatch : BytecodePatch(
setOf(
CreateShortsButtonsFingerprint,
ReelConstructorFingerprint,
BottomNavigationBarFingerprint,
ShortsBottomBarContainerFingerprint,
LegacyRenderBottomNavigationBarParentFingerprint,
RenderBottomNavigationBarParentFingerprint,
SetPivotBarVisibilityParentFingerprint,
),
@ -95,29 +84,30 @@ object HideShortsComponentsPatch : BytecodePatch(
// region Hide the Shorts shelf.
// This patch point is not present in 19.03.x and greater.
// If 19.02.x and lower is dropped, then this section of code and the fingerprint should be removed.
ReelConstructorFingerprint.result?.let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2
val viewRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
if (!VersionCheckPatch.is_19_03_or_greater) {
ReelConstructorFingerprint.result?.let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2
val viewRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
injectHideViewCall(
insertIndex,
viewRegister,
FILTER_CLASS_DESCRIPTOR,
"hideShortsShelf",
)
injectHideViewCall(
insertIndex,
viewRegister,
FILTER_CLASS_DESCRIPTOR,
"hideShortsShelf",
)
}
}
} // Do not throw an exception if not resolved.
}
// endregion
// region Hide the Shorts buttons in older versions of YouTube.
// Some Shorts buttons are views, hide them by setting their visibility to GONE.
CreateShortsButtonsFingerprint.result?.let {
CreateShortsButtonsFingerprint.resultOrThrow().let {
ShortsButtons.entries.forEach { button -> button.injectHideCall(it.mutableMethod) }
} ?: throw CreateShortsButtonsFingerprint.exception
}
// endregion
@ -125,54 +115,68 @@ object HideShortsComponentsPatch : BytecodePatch(
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
context.forEachLiteralValueInstruction(
HideShortsComponentsResourcePatch.reelPlayerRightPivotV2Size
) { literalInstructionIndex ->
val targetIndex = indexOfFirstInstructionOrThrow(literalInstructionIndex) {
getReference<MethodReference>()?.name == "getDimensionPixelSize"
} + 1
val sizeRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(targetIndex + 1, """
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
move-result v$sizeRegister
"""
)
}
// endregion
// region Hide the navigation bar.
// Hook to get the pivotBar view.
SetPivotBarVisibilityParentFingerprint.result?.let {
if (!SetPivotBarVisibilityFingerprint.resolve(context, it.classDef)) {
throw SetPivotBarVisibilityFingerprint.exception
}
SetPivotBarVisibilityFingerprint.result!!.let { result ->
result.mutableMethod.apply {
val insertIndex = result.scanResult.patternScanResult!!.endIndex
val viewRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
addInstruction(
insertIndex,
"sput-object v$viewRegister, $FILTER_CLASS_DESCRIPTOR->pivotBar:" +
"Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;",
)
}
}
} ?: throw SetPivotBarVisibilityParentFingerprint.exception
// Hook to hide the navigation bar when Shorts are being played.
RenderBottomNavigationBarParentFingerprint.result?.let {
if (!RenderBottomNavigationBarFingerprint.resolve(context, it.classDef)) {
throw RenderBottomNavigationBarFingerprint.exception
}
RenderBottomNavigationBarFingerprint.result!!.mutableMethod.apply {
addInstruction(0, "invoke-static { }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar()V")
}
} ?: throw RenderBottomNavigationBarParentFingerprint.exception
// Required to prevent a black bar from appearing at the bottom of the screen.
BottomNavigationBarFingerprint.result?.let {
it.mutableMethod.apply {
val moveResultIndex = it.scanResult.patternScanResult!!.startIndex + 2
val viewRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
val insertIndex = moveResultIndex + 1
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, $FILTER_CLASS_DESCRIPTOR->" +
"hideNavigationBar(Landroid/view/View;)Landroid/view/View;",
SetPivotBarVisibilityFingerprint.alsoResolve(
context,
SetPivotBarVisibilityParentFingerprint
).let { result->
result.mutableMethod.apply {
val insertIndex = result.scanResult.patternScanResult!!.endIndex
val viewRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
addInstruction(insertIndex, "invoke-static {v$viewRegister}," +
" $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V"
)
}
} ?: throw BottomNavigationBarFingerprint.exception
}
// Hook to hide the shared navigation bar when the Shorts player is opened.
RenderBottomNavigationBarFingerprint.alsoResolve(
context,
if (VersionCheckPatch.is_19_41_or_greater)
RenderBottomNavigationBarParentFingerprint
else
LegacyRenderBottomNavigationBarParentFingerprint
).mutableMethod.addInstruction(
0,
"invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V"
)
// Hide the bottom bar container of the Shorts player.
ShortsBottomBarContainerFingerprint.resultOrThrow().mutableMethod.apply {
val resourceIndex = indexOfFirstWideLiteralInstructionValue(HideShortsComponentsResourcePatch.bottomBarContainer)
val targetIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
getReference<MethodReference>()?.name == "getHeight"
} + 1
val heightRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(targetIndex + 1, """
invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I
move-result v$heightRegister
"""
)
}
// endregion
}
@ -187,14 +191,12 @@ object HideShortsComponentsPatch : BytecodePatch(
fun injectHideCall(method: MutableMethod) {
val referencedIndex = method.indexOfIdResourceOrThrow(resourceName)
val instruction = method.implementation!!.instructions
.subList(referencedIndex, referencedIndex + 20)
.first {
it.opcode == Opcode.INVOKE_VIRTUAL && it.getReference<MethodReference>()?.name == "setId"
}
val setIdIndex = method.indexOfFirstInstructionOrThrow(referencedIndex) {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setId"
}
val setIdIndex = instruction.location.index
val viewRegister = method.getInstruction<FiveRegisterInstruction>(setIdIndex).registerC
method.injectHideViewCall(setIdIndex + 1, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName)
}
}

View File

@ -8,13 +8,23 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsAppShortcut
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsWidget
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.findElementByAttributeValueOrThrow
@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class, AddResourcesPatch::class])
@Patch(
dependencies = [
SettingsPatch::class,
ResourceMappingPatch::class,
AddResourcesPatch::class,
VersionCheckPatch::class
]
)
object HideShortsComponentsResourcePatch : ResourcePatch() {
internal var reelMultipleItemShelfId = -1L
internal var reelPlayerRightCellButtonHeight = -1L
internal var bottomBarContainer = -1L
internal var reelPlayerRightPivotV2Size = -1L
override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class)
@ -29,6 +39,7 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
// since this Setting menu currently uses the ordering used here.
// Vertical row of buttons on right side of the screen.
SwitchPreference("revanced_hide_shorts_like_fountain"),
SwitchPreference("revanced_hide_shorts_like_button"),
SwitchPreference("revanced_hide_shorts_dislike_button"),
SwitchPreference("revanced_hide_shorts_comments_button"),
@ -41,6 +52,10 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
SwitchPreference("revanced_hide_shorts_subscribe_button"),
SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"),
SwitchPreference("revanced_hide_shorts_save_sound_button"),
SwitchPreference("revanced_hide_shorts_use_template_button"),
SwitchPreference("revanced_hide_shorts_upcoming_button"),
SwitchPreference("revanced_hide_shorts_green_screen_button"),
SwitchPreference("revanced_hide_shorts_hashtag_button"),
SwitchPreference("revanced_hide_shorts_shop_button"),
SwitchPreference("revanced_hide_shorts_tagged_products"),
SwitchPreference("revanced_hide_shorts_stickers"),
@ -59,7 +74,7 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
context.xmlEditor["res/xml/main_shortcuts.xml"].use { editor ->
val shortsItem = editor.file.childNodes.findElementByAttributeValueOrThrow(
"android:shortcutId",
"shorts-shortcut"
"shorts-shortcut",
)
shortsItem.parentNode.removeChild(shortsItem)
@ -70,7 +85,7 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
context.xmlEditor["res/layout/appwidget_two_rows.xml"].use { editor ->
val shortsItem = editor.file.childNodes.findElementByAttributeValueOrThrow(
"android:id",
"@id/button_shorts_container"
"@id/button_shorts_container",
)
shortsItem.parentNode.removeChild(shortsItem)
@ -82,14 +97,21 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
"reel_player_right_cell_button_height",
]
// Resource not present in new versions of the app.
try {
ResourceMappingPatch[
bottomBarContainer = ResourceMappingPatch[
"id",
"bottom_bar_container"
]
reelPlayerRightPivotV2Size = ResourceMappingPatch[
"dimen",
"reel_player_right_pivot_v2_size"
]
if (!VersionCheckPatch.is_19_03_or_greater) {
reelMultipleItemShelfId = ResourceMappingPatch[
"dimen",
"reel_player_right_cell_button_height",
]
} catch (e: NoSuchElementException) {
return
}.also { reelPlayerRightCellButtonHeight = it }
}
}
}

View File

@ -1,24 +0,0 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
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
internal object BottomNavigationBarFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;", "Landroid/os/Bundle;"),
opcodes = listOf(
Opcode.CONST, // R.id.app_engagement_panel_wrapper
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
),
strings = listOf(
"ReelWatchPaneFragmentViewModelKey"
),
)

View File

@ -0,0 +1,15 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object LegacyRenderBottomNavigationBarParentFingerprint : MethodFingerprint(
parameters = listOf(
"I",
"I",
"L",
"L",
"J",
"L",
),
strings = listOf("aa")
)

View File

@ -1,19 +1,13 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsResourcePatch
import app.revanced.util.containsWideLiteralInstructionValue
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object ReelConstructorFingerprint : MethodFingerprint(
internal object ReelConstructorFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(Opcode.INVOKE_VIRTUAL),
customFingerprint = { methodDef, _ ->
// Cannot use LiteralValueFingerprint, because the resource id may not be present.
val reelMultipleItemShelfId = HideShortsComponentsResourcePatch.reelMultipleItemShelfId
reelMultipleItemShelfId != -1L
&& methodDef.containsWideLiteralInstructionValue(reelMultipleItemShelfId)
}
literalSupplier = { HideShortsComponentsResourcePatch.reelMultipleItemShelfId }
)

View File

@ -4,6 +4,8 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object RenderBottomNavigationBarFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("Ljava/lang/String;"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.MONITOR_ENTER,

View File

@ -1,8 +1,23 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Identical to [LegacyRenderBottomNavigationBarParentFingerprint]
* except this has an extra parameter.
*/
internal object RenderBottomNavigationBarParentFingerprint : MethodFingerprint(
parameters = listOf("I", "I", "L", "L", "J", "L"),
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf(
"I",
"I",
"L", // ReelWatchEndpointOuterClass
"L",
"J",
"Ljava/lang/String;",
"L"
),
strings = listOf("aa")
)

View File

@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object ShortsBottomBarContainerFingerprint : LiteralValueFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;", "Landroid/os/Bundle;"),
strings = listOf(
"r_pfvc"
),
literalSupplier = { HideShortsComponentsResourcePatch.bottomBarContainer }
)

View File

@ -18,29 +18,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -19,29 +19,11 @@ import app.revanced.util.exception
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -1,17 +1,20 @@
@file:Suppress("SpellCheckingInspection")
package app.revanced.patches.youtube.layout.miniplayer
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.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
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.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
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.InputType
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
@ -26,6 +29,7 @@ import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.sc
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlineXWhite24
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerDimensionsCalculatorParentFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerMinimumSizeFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernAddViewListenerFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernCloseButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernConstructorFingerprint
@ -42,30 +46,32 @@ import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayer
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.alsoResolve
import app.revanced.util.findOpcodeIndicesReversed
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.patch.LiteralValueFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
// YT uses "Miniplayer" without a space between 'mini' and 'player: https://support.google.com/youtube/answer/9162927.
// YT uses "Miniplayer" without a space between 'mini' and 'player': https://support.google.com/youtube/answer/9162927.
@Patch(
name = "Miniplayer",
description = "Adds options to change the in app minimized player, " +
"and if patching target 19.16+ adds options to use modern miniplayers.",
description = "Adds options to change the in app minimized player. " +
"Patching target 19.16+ adds modern miniplayers.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
@ -75,32 +81,28 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
// 19.14 is left out, as it has incomplete miniplayer code and missing some UI resources.
// It's simpler to not bother with supporting this single old version.
// 19.15 has a different code for handling sub title texts,
// and also probably not worth making changes just to support this single old version.
"19.16.39" // Earliest supported version with modern miniplayers.
// 19.14.43 // Incomplete code for modern miniplayers.
// 19.15.36 // Different code for handling subtitle texts and not worth supporting.
"19.16.39", // First with modern miniplayers.
// 19.17.41 // Works without issues, but no reason to recommend over 19.16.
// 19.18.41 // Works without issues, but no reason to recommend over 19.16.
// 19.19.39 // Last bug free version with smaller Modern 1 miniplayer, but no reason to recommend over 19.16.
// 19.20.35 // Cannot swipe to expand.
// 19.21.40 // Cannot swipe to expand.
// 19.22.43 // Cannot swipe to expand.
// 19.23.40 // First with Modern 1 drag and drop, Cannot swipe to expand.
// 19.24.45 // First with larger Modern 1, Cannot swipe to expand.
"19.25.37", // First with double tap, last with skip forward/back buttons, last with swipe to expand/close, and last before double tap to expand seems to be required.
// 19.26.42 // Modern 1 Pause/play button are always hidden. Unusable.
// 19.28.42 // First with custom miniplayer size, screen flickers when swiping to maximize Modern 1. Swipe to close miniplayer is broken.
// 19.29.42 // All modern players are broken and ignore tapping the miniplayer video.
// 19.30.39 // Modern 3 is less broken when double tap expand is enabled, but cannot swipe to expand when double tap is off.
// 19.31.36 // All Modern 1 buttons are missing. Unusable.
// 19.32.36 // 19.32+ and beyond all work without issues.
// 19.33.35
"19.34.42",
]
)
]
@ -113,6 +115,7 @@ object MiniplayerPatch : BytecodePatch(
MiniplayerOverrideFingerprint,
MiniplayerModernConstructorFingerprint,
MiniplayerModernViewParentFingerprint,
MiniplayerMinimumSizeFingerprint,
YouTubePlayerOverlaysLayoutFingerprint
)
) {
@ -121,48 +124,71 @@ object MiniplayerPatch : BytecodePatch(
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
// Modern mini player is only present and functional in 19.15+.
// Resource is not present in older versions. Using it to determine, if patching an old version.
val isPatchingOldVersion = ytOutlinePictureInPictureWhite24 < 0
val preferences = mutableSetOf<BasePreference>()
if (!VersionCheckPatch.is_19_16_or_greater) {
preferences += ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
entriesKey = "revanced_miniplayer_type_legacy_entries",
entryValuesKey = "revanced_miniplayer_type_legacy_entry_values"
)
} else {
preferences += ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
)
if (VersionCheckPatch.is_19_25_or_greater) {
if (!VersionCheckPatch.is_19_29_or_greater) {
preferences += SwitchPreference("revanced_miniplayer_double_tap_action")
}
preferences += SwitchPreference("revanced_miniplayer_drag_and_drop")
}
if (VersionCheckPatch.is_19_36_or_greater) {
preferences += SwitchPreference("revanced_miniplayer_rounded_corners")
}
preferences += SwitchPreference("revanced_miniplayer_hide_subtext")
preferences +=
if (VersionCheckPatch.is_19_26_or_greater) {
SwitchPreference("revanced_miniplayer_hide_expand_close")
} else {
SwitchPreference(
key = "revanced_miniplayer_hide_expand_close",
titleKey = "revanced_miniplayer_hide_expand_close_legacy_title",
summaryOnKey = "revanced_miniplayer_hide_expand_close_legacy_summary_on",
summaryOffKey = "revanced_miniplayer_hide_expand_close_legacy_summary_off",
)
}
if (!VersionCheckPatch.is_19_26_or_greater) {
preferences += SwitchPreference("revanced_miniplayer_hide_rewind_forward")
}
if (VersionCheckPatch.is_19_26_or_greater) {
preferences += TextPreference("revanced_miniplayer_width_dip", inputType = InputType.NUMBER)
}
preferences += TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
}
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen(
key = "revanced_miniplayer_screen",
sorting = Sorting.UNSORTED,
preferences =
if (isPatchingOldVersion) {
setOf(
ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
entriesKey = "revanced_miniplayer_type_legacy_entries",
entryValuesKey = "revanced_miniplayer_type_legacy_entry_values"
)
)
} else {
setOf(
ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
entriesKey = "revanced_miniplayer_type_19_15_entries",
entryValuesKey = "revanced_miniplayer_type_19_15_entry_values"
),
SwitchPreference("revanced_miniplayer_hide_expand_close"),
SwitchPreference("revanced_miniplayer_hide_subtext"),
SwitchPreference("revanced_miniplayer_hide_rewind_forward"),
TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
)
}
preferences = preferences
)
)
// region Enable tablet miniplayer.
MiniplayerOverrideNoContextFingerprint.resolve(
MiniplayerOverrideNoContextFingerprint.alsoResolve(
context,
MiniplayerDimensionsCalculatorParentFingerprint.resultOrThrow().classDef
)
MiniplayerOverrideNoContextFingerprint.resultOrThrow().mutableMethod.apply {
MiniplayerDimensionsCalculatorParentFingerprint
).mutableMethod.apply {
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
}
@ -188,8 +214,8 @@ object MiniplayerPatch : BytecodePatch(
it.mutableMethod.insertLegacyTabletMiniplayerOverride(it.scanResult.patternScanResult!!.endIndex)
}
if (isPatchingOldVersion) {
// Return here, as patch below is only intended for new versions of the app.
if (!VersionCheckPatch.is_19_16_or_greater) {
// Return here, as patch below is only for the current versions of the app.
return
}
@ -212,59 +238,150 @@ object MiniplayerPatch : BytecodePatch(
}
}
if (VersionCheckPatch.is_19_23_or_greater) {
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL,
"enableMiniplayerDragAndDrop"
)
}
if (VersionCheckPatch.is_19_25_or_greater) {
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY,
"getModernMiniplayerOverride"
)
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL,
"getModernFeatureFlagsActiveOverride"
)
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL,
"enableMiniplayerDoubleTapAction"
)
}
if (VersionCheckPatch.is_19_26_or_greater) {
MiniplayerModernConstructorFingerprint.resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(
MiniplayerModernConstructorFingerprint.INITIAL_SIZE_FEATURE_KEY_LITERAL
)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT)
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->setMiniplayerDefaultSize(I)I
move-result v$register
"""
)
}
// Override a mininimum miniplayer size constant.
MiniplayerMinimumSizeFingerprint.resultOrThrow().mutableMethod.apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.CONST_16 && (this as NarrowLiteralInstruction).narrowLiteral == 192
}
val register = getInstruction<OneRegisterInstruction>(index).registerA
// Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller.
// The 170 initial limit probably could be patched to allow even smaller initial sizes,
// but 170 is already half the horizontal space and smaller does not seem useful.
replaceInstruction(index, "const/16 v$register, 170")
}
}
if (VersionCheckPatch.is_19_32_or_greater) {
// Feature is not exposed in the settings, and currently only for debugging.
MiniplayerModernConstructorFingerprint.insertLiteralValueFloatOverride(
MiniplayerModernConstructorFingerprint.ANIMATION_INTERPOLATION_FEATURE_KEY,
"setMovementBoundFactor"
)
}
if (VersionCheckPatch.is_19_36_or_greater) {
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.DROP_SHADOW_FEATURE_KEY,
"setDropShadow"
)
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.ROUNDED_CORNERS_FEATURE_KEY,
"setRoundedCorners"
)
}
// endregion
// region Fix 19.16 using mixed up drawables for tablet modern.
// YT fixed this mistake in 19.17.
// Fix this, by swapping the drawable resource values with each other.
MiniplayerModernExpandCloseDrawablesFingerprint.apply {
resolve(
if (ytOutlinePictureInPictureWhite24 >= 0) {
MiniplayerModernExpandCloseDrawablesFingerprint.alsoResolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
}.resultOrThrow().mutableMethod.apply {
listOf(
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
).forEach { (originalResource, replacementResource) ->
val imageResourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(originalResource)
val register = getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
MiniplayerModernViewParentFingerprint
).mutableMethod.apply {
listOf(
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
).forEach { (originalResource, replacementResource) ->
val imageResourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(originalResource)
val register = getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
replaceInstruction(imageResourceIndex, "const v$register, $replacementResource")
replaceInstruction(imageResourceIndex, "const v$register, $replacementResource")
}
}
}
// endregion
// region Add hooks to hide tablet modern miniplayer buttons.
// region Add hooks to hide modern miniplayer buttons.
listOf(
Triple(MiniplayerModernExpandButtonFingerprint, modernMiniplayerExpand,"hideMiniplayerExpandClose"),
Triple(MiniplayerModernCloseButtonFingerprint, modernMiniplayerClose, "hideMiniplayerExpandClose"),
Triple(MiniplayerModernRewindButtonFingerprint, modernMiniplayerRewindButton, "hideMiniplayerRewindForward"),
Triple(MiniplayerModernForwardButtonFingerprint, modernMiniplayerForwardButton, "hideMiniplayerRewindForward"),
Triple(MiniplayerModernOverlayViewFingerprint, scrimOverlay, "adjustMiniplayerOpacity")
).forEach { (fingerprint, literalValue, methodName) ->
fingerprint.resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
Triple(
MiniplayerModernExpandButtonFingerprint,
modernMiniplayerExpand,
"hideMiniplayerExpandClose"
),
Triple(
MiniplayerModernCloseButtonFingerprint,
modernMiniplayerClose,
"hideMiniplayerExpandClose"
),
Triple(
MiniplayerModernRewindButtonFingerprint,
modernMiniplayerRewindButton,
"hideMiniplayerRewindForward"
),
Triple(
MiniplayerModernForwardButtonFingerprint,
modernMiniplayerForwardButton,
"hideMiniplayerRewindForward"
),
Triple(
MiniplayerModernOverlayViewFingerprint,
scrimOverlay,
"adjustMiniplayerOpacity"
)
fingerprint.hookInflatedView(
).forEach { (fingerprint, literalValue, methodName) ->
fingerprint.alsoResolve(
context,
MiniplayerModernViewParentFingerprint
).mutableMethod.hookInflatedView(
literalValue,
"Landroid/widget/ImageView;",
"$INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
)
}
MiniplayerModernAddViewListenerFingerprint.apply {
resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
}.resultOrThrow().mutableMethod.addInstruction(
MiniplayerModernAddViewListenerFingerprint.alsoResolve(
context,
MiniplayerModernViewParentFingerprint
).mutableMethod.addInstruction(
0,
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" +
"hideMiniplayerSubTexts(Landroid/view/View;)V"
@ -275,6 +392,9 @@ object MiniplayerPatch : BytecodePatch(
// Modern 2 uses the same overlay controls as the regular video player,
// and the overlay views are added at runtime.
// Add a hook to the overlay class, and pass the added views to integrations.
//
// NOTE: Modern 2 uses the same video UI as the regular player except resized to smaller.
// This patch code could be used to hide other player overlays that do not use Litho.
YouTubePlayerOverlaysLayoutFingerprint.resultOrThrow().mutableClass.methods.add(
ImmutableMethod(
YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME,
@ -319,6 +439,37 @@ object MiniplayerPatch : BytecodePatch(
insertBooleanOverride(index, "getModernMiniplayerOverride")
}
private fun MethodFingerprint.insertLiteralValueBooleanOverride(
literal: Long,
integrationsMethod: String
) {
resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(literal)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
insertBooleanOverride(targetIndex + 1, integrationsMethod)
}
}
private fun MethodFingerprint.insertLiteralValueFloatOverride(
literal: Long,
integrationsMethod: String
) {
resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(literal)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->$integrationsMethod(F)F
move-result v$register
"""
)
}
}
private fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) {
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
@ -335,36 +486,30 @@ object MiniplayerPatch : BytecodePatch(
*/
private fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
val targetReference = (targetInstruction as ReferenceInstruction).reference
addInstructions(
iPutIndex + 1, """
addInstructionsAtControlFlowLabel(
iPutIndex, """
invoke-static { v${targetInstruction.registerA} }, $INTEGRATIONS_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
move-result v${targetInstruction.registerA}
# Original instruction
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
"""
)
removeInstruction(iPutIndex)
}
private fun LiteralValueFingerprint.hookInflatedView(
private fun MutableMethod.hookInflatedView(
literalValue: Long,
hookedClassType: String,
integrationsMethodName: String,
) {
resultOrThrow().mutableMethod.apply {
val imageViewIndex = indexOfFirstInstructionOrThrow(
indexOfFirstWideLiteralInstructionValueOrThrow(literalValue)
) {
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type == hookedClassType
}
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $integrationsMethodName"
)
val imageViewIndex = indexOfFirstInstructionOrThrow(
indexOfFirstWideLiteralInstructionValueOrThrow(literalValue)
) {
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type == hookedClassType
}
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $integrationsMethodName"
)
}
}

View File

@ -1,12 +1,12 @@
package app.revanced.patches.youtube.layout.miniplayer
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
@Patch(dependencies = [ResourceMappingPatch::class])
@Patch(dependencies = [ResourceMappingPatch::class, VersionCheckPatch::class])
internal object MiniplayerResourcePatch : ResourcePatch() {
var floatyBarButtonTopMargin = -1L
@ -19,6 +19,7 @@ internal object MiniplayerResourcePatch : ResourcePatch() {
var modernMiniplayerRewindButton = -1L
var modernMiniplayerForwardButton = -1L
var playerOverlays = -1L
var miniplayerMaxSize = -1L
override fun execute(context: ResourceContext) {
floatyBarButtonTopMargin = ResourceMappingPatch[
@ -26,56 +27,63 @@ internal object MiniplayerResourcePatch : ResourcePatch() {
"floaty_bar_button_top_margin"
]
try {
ytOutlinePictureInPictureWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_picture_in_picture_white_24"
]
} catch (exception: PatchException) {
// Ignore, and assume the app is 19.14 or earlier.
return
}
ytOutlineXWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_x_white_24"
]
scrimOverlay = ResourceMappingPatch[
"id",
"scrim_overlay"
]
modernMiniplayerClose = ResourceMappingPatch[
"id",
"modern_miniplayer_close"
]
modernMiniplayerExpand = ResourceMappingPatch[
"id",
"modern_miniplayer_expand"
]
modernMiniplayerRewindButton = ResourceMappingPatch[
"id",
"modern_miniplayer_rewind_button"
]
modernMiniplayerForwardButton = ResourceMappingPatch[
"id",
"modern_miniplayer_forward_button"
]
playerOverlays = ResourceMappingPatch[
"layout",
"player_overlays"
]
// Resource id is not used during patching, but is used by integrations.
// Verify the resource is present while patching.
ResourceMappingPatch[
"id",
"modern_miniplayer_subtitle_text"
]
if (VersionCheckPatch.is_19_16_or_greater) {
modernMiniplayerClose = ResourceMappingPatch[
"id",
"modern_miniplayer_close"
]
modernMiniplayerExpand = ResourceMappingPatch[
"id",
"modern_miniplayer_expand"
]
modernMiniplayerRewindButton = ResourceMappingPatch[
"id",
"modern_miniplayer_rewind_button"
]
modernMiniplayerForwardButton = ResourceMappingPatch[
"id",
"modern_miniplayer_forward_button"
]
// Resource id is not used during patching, but is used by integrations.
// Verify the resource is present while patching.
ResourceMappingPatch[
"id",
"modern_miniplayer_subtitle_text"
]
// Only required for exactly 19.16
if (!VersionCheckPatch.is_19_17_or_greater) {
ytOutlinePictureInPictureWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_picture_in_picture_white_24"
]
ytOutlineXWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_x_white_24"
]
}
if (VersionCheckPatch.is_19_26_or_greater) {
miniplayerMaxSize = ResourceMappingPatch[
"dimen",
"miniplayer_max_size"
]
}
}
}
}

View File

@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.containsWideLiteralInstructionValue
import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniplayerMinimumSizeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionValue(192)
&& methodDef.containsWideLiteralInstructionValue(128)
&& methodDef.containsWideLiteralInstructionValue(MiniplayerResourcePatch.miniplayerMaxSize)
}
)

View File

@ -8,4 +8,14 @@ internal object MiniplayerModernConstructorFingerprint : LiteralValueFingerprint
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("L"),
literalSupplier = { 45623000L } // Magic number found in the constructor.
)
) {
const val MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL = 45622882L
// In later targets this feature flag does nothing and is dead code.
const val MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY = 45630429L
const val DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL = 45628823L
const val DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL = 45628752L
const val INITIAL_SIZE_FEATURE_KEY_LITERAL = 45640023L
const val ANIMATION_INTERPOLATION_FEATURE_KEY = 45647018L
const val DROP_SHADOW_FEATURE_KEY = 45652223L
const val ROUNDED_CORNERS_FEATURE_KEY = 45652224L
}

View File

@ -7,6 +7,5 @@ import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniplayerOverrideFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
strings = listOf("appName")
)

View File

@ -19,30 +19,11 @@ import app.revanced.util.exception
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -14,30 +14,11 @@ import org.w3c.dom.Element
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
@ -27,11 +28,14 @@ import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.Tex
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.shared.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.alsoResolve
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@ -51,27 +55,15 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
VideoIdPatch::class,
ReturnYouTubeDislikeResourcePatch::class,
PlayerTypeHookPatch::class,
VersionCheckPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
@ -117,7 +109,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
DislikeFingerprint.toPatch(Vote.DISLIKE),
RemoveLikeFingerprint.toPatch(Vote.REMOVE_LIKE)
).forEach { (fingerprint, vote) ->
fingerprint.result?.mutableMethod?.apply {
fingerprint.resultOrThrow().mutableMethod.apply {
addInstructions(
0,
"""
@ -125,7 +117,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->sendVote(I)V
"""
)
} ?: throw fingerprint.exception
}
}
// endregion
@ -136,7 +128,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
// And it works in all situations except it fails to update the Span when the user dislikes,
// since the underlying (likes only) text did not change.
// This hook handles all situations, as it's where the created Spans are stored and later reused.
TextComponentConstructorFingerprint.result?.let { textConstructorResult ->
TextComponentConstructorFingerprint.resultOrThrow().let { textConstructorResult ->
// Find the field name of the conversion context.
val conversionContextClassType = ConversionContextFingerprint.resultOrThrow().classDef.type
val conversionContextField = textConstructorResult.classDef.fields.find {
@ -147,55 +139,80 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
TextComponentLookupFingerprint.resultOrThrow().mutableMethod.apply {
// Find the instruction for creating the text data object.
val textDataClassType = TextComponentDataFingerprint.resultOrThrow().classDef.type
val insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.NEW_INSTANCE &&
getReference<TypeReference>()?.type == textDataClassType
val insertIndex : Int
val tempRegister : Int
val charSequenceRegister : Int
if (VersionCheckPatch.is_19_33_or_greater) {
insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC_RANGE &&
getReference<MethodReference>()?.returnType == textDataClassType
}
tempRegister = getInstruction<OneRegisterInstruction>(insertIndex + 1).registerA
// Find the instruction that sets the span to an instance field.
// The instruction is only a few lines after the creation of the instance.
charSequenceRegister = getInstruction<FiveRegisterInstruction>(
indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;"
}
).registerD
} else {
insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.NEW_INSTANCE &&
getReference<TypeReference>()?.type == textDataClassType
}
tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
charSequenceRegister = getInstruction<TwoRegisterInstruction>(
indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/CharSequence;"
}
).registerA
}
val tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// Find the instruction that sets the span to an instance field.
// The instruction is only a few lines after the creation of the instance.
// The method has multiple iput-object instructions using a CharSequence,
// so verify the found instruction is in the expected location.
val putFieldInstruction = implementation!!.instructions
.subList(insertIndex, insertIndex + 20)
.find {
it.opcode == Opcode.IPUT_OBJECT &&
it.getReference<FieldReference>()?.type == "Ljava/lang/CharSequence;"
} ?: throw PatchException("Could not find put object instruction")
val charSequenceRegister = (putFieldInstruction as TwoRegisterInstruction).registerA
addInstructions(
insertIndex,
addInstructionsAtControlFlowLabel(insertIndex,
"""
# Copy conversion context
move-object/from16 v$tempRegister, p0
iget-object v$tempRegister, v$tempRegister, $conversionContextField
invoke-static { v$tempRegister, v$charSequenceRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister
"""
# Copy conversion context
move-object/from16 v$tempRegister, p0
iget-object v$tempRegister, v$tempRegister, $conversionContextField
invoke-static {v$tempRegister, v$charSequenceRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister
"""
)
}
} ?: throw TextComponentConstructorFingerprint.exception
}
// endregion
// region Hook for non-litho Short videos.
ShortsTextViewFingerprint.result?.let {
ShortsTextViewFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val patternResult = it.scanResult.patternScanResult!!
val insertIndex = it.scanResult.patternScanResult!!.endIndex + 1
// If the field is true, the TextView is for a dislike button.
val isDisLikesBooleanReference = getInstruction<ReferenceInstruction>(patternResult.endIndex).reference
val isDisLikesBooleanInstruction = getInstructions().first { instruction ->
instruction.opcode == Opcode.IGET_BOOLEAN
} as ReferenceInstruction
val textViewFieldReference = // Like/Dislike button TextView field
getInstruction<ReferenceInstruction>(patternResult.endIndex - 1).reference
val isDisLikesBooleanReference = isDisLikesBooleanInstruction.reference
// Like/Dislike button TextView field.
val textViewFieldInstruction = getInstructions().first { instruction ->
instruction.opcode == Opcode.IGET_OBJECT
} as ReferenceInstruction
val textViewFieldReference = textViewFieldInstruction.reference
// Check if the hooked TextView object is that of the dislike button.
// If RYD is disabled, or the TextView object is not that of the dislike button, the execution flow is not interrupted.
// Otherwise, the TextView object is modified, and the execution flow is interrupted to prevent it from being changed afterward.
val insertIndex = patternResult.startIndex + 6
addInstructionsWithLabels(
insertIndex,
"""
@ -216,7 +233,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
"""
)
}
} ?: throw ShortsTextViewFingerprint.exception
}
// endregion
@ -251,20 +268,14 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
// region Hook rolling numbers.
// Do this last to allow patching old unsupported versions (if the user really wants),
// On older unsupported version this will fail to resolve and throw an exception,
// but everything will still work correctly anyways.
RollingNumberSetterFingerprint.result?.let {
RollingNumberSetterFingerprint.resultOrThrow().let {
val dislikesIndex = it.scanResult.patternScanResult!!.endIndex
it.mutableMethod.apply {
val insertIndex = 1
val charSequenceInstanceRegister =
getInstruction<OneRegisterInstruction>(0).registerA
val charSequenceFieldReference =
getInstruction<ReferenceInstruction>(dislikesIndex).reference
val charSequenceInstanceRegister = getInstruction<OneRegisterInstruction>(0).registerA
val charSequenceFieldReference = getInstruction<ReferenceInstruction>(dislikesIndex).reference
val registerCount = implementation!!.registerCount
@ -282,7 +293,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
"""
)
}
} ?: throw RollingNumberSetterFingerprint.exception
}
// Rolling Number text views use the measured width of the raw string for layout.
// Modify the measure text calculation to include the left drawable separator if needed.
@ -306,9 +317,12 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
// Additional text measurement method. Used if YouTube decides not to animate the likes count
// and sometimes used for initial video load.
RollingNumberMeasureStaticLabelFingerprint.resolve(context, RollingNumberMeasureStaticLabelParentFingerprint.resultOrThrow().classDef)
RollingNumberMeasureStaticLabelFingerprint.result?.also {
RollingNumberMeasureStaticLabelFingerprint.alsoResolve(
context,
RollingNumberMeasureStaticLabelParentFingerprint
).let {
val measureTextIndex = it.scanResult.patternScanResult!!.startIndex + 1
it.mutableMethod.apply {
val freeRegister = getInstruction<TwoRegisterInstruction>(0).registerA
@ -320,19 +334,18 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
"""
)
}
} ?: throw RollingNumberMeasureStaticLabelFingerprint.exception
}
// The rolling number Span is missing styling since it's initially set as a String.
// Modify the UI text view and use the styled like/dislike Span.
RollingNumberTextViewFingerprint.result?.let {
RollingNumberTextViewFingerprint.resultOrThrow().let {
// Initial TextView is set in this method.
val initiallyCreatedTextViewMethod = it.mutableMethod
// Videos less than 24 hours after uploaded, like counts will be updated in real time.
// Whenever like counts are updated, TextView is set in this method.
val realTimeUpdateTextViewMethod =
RollingNumberTextViewAnimationUpdateFingerprint.result?.mutableMethod
?: throw RollingNumberTextViewAnimationUpdateFingerprint.exception
RollingNumberTextViewAnimationUpdateFingerprint.resultOrThrow().mutableMethod
arrayOf(
initiallyCreatedTextViewMethod,
@ -357,7 +370,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
)
}
}
} ?: throw RollingNumberTextViewFingerprint.exception
}
// endregion

View File

@ -10,9 +10,6 @@ internal object ConversionContextFingerprint : MethodFingerprint(
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
// 18.37.36 and after this String is: ConversionContext{containerInternal=
// and before it is: ConversionContext{container=
// Use a partial string to match both.
"ConversionContext{container"
"ConversionContext{containerInternal="
)
)

View File

@ -8,5 +8,6 @@ internal object RollingNumberSetterFingerprint : MethodFingerprint(
Opcode.INVOKE_DIRECT,
Opcode.IGET_OBJECT
),
strings = listOf("RollingNumberType required properties missing! Need updateCount, fontName, color and fontSize.")
// Partial string match.
strings = listOf("RollingNumberType required properties missing! Need")
)

View File

@ -18,6 +18,7 @@ internal object RollingNumberTextViewFingerprint : MethodFingerprint(
Opcode.RETURN_VOID
),
customFingerprint = { _, classDef ->
classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;"
classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" ||
classDef.superclass == "Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;"
}
)

View File

@ -15,18 +15,6 @@ internal object ShortsTextViewFingerprint : MethodFingerprint(
null,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.SGET_OBJECT, // insertion point, must be after constructor call to parent class
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.CONST_4,
Opcode.IF_EQ,
Opcode.CONST_4,
Opcode.IF_EQ,
Opcode.RETURN_VOID,
Opcode.IGET_OBJECT, // TextView field
Opcode.IGET_BOOLEAN, // boolean field
Opcode.CHECK_CAST
)
)

View File

@ -9,8 +9,6 @@ internal object TextComponentDataFingerprint : MethodFingerprint(
parameters = listOf("L", "L"),
strings = listOf("text"),
customFingerprint = { _, classDef ->
val fields = classDef.fields
fields.find { it.type == "Ljava/util/BitSet;" } != null &&
fields.find { it.type == "[Ljava/lang/String;" } != null
classDef.fields.find { it.type == "Ljava/util/BitSet;" } != null
}
)

View File

@ -24,29 +24,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -18,6 +18,6 @@ internal object SetWordmarkHeaderFingerprint : MethodFingerprint(
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.CONST,
Opcode.INVOKE_STATIC,
null // invoke-static or invoke-virtual
)
)

View File

@ -4,45 +4,28 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.seekbar.fingerprints.FullscreenSeekbarThumbnailsFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
@Patch(
name = "Restore old seekbar thumbnails",
description = "Adds an option to restore the old seekbar thumbnails that appear above the seekbar while seeking instead of fullscreen thumbnails.",
dependencies = [IntegrationsPatch::class, AddResourcesPatch::class],
dependencies = [IntegrationsPatch::class, AddResourcesPatch::class, VersionCheckPatch::class],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.16.39"
// 19.17+ is not supported.
]
)
]
@ -55,6 +38,11 @@ object RestoreOldSeekbarThumbnailsPatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/patches/RestoreOldSeekbarThumbnailsPatch;"
override fun execute(context: BytecodeContext) {
if (VersionCheckPatch.is_19_17_or_greater) {
// Give a more informative error, if the user has turned off version checks.
throw PatchException("'Restore old seekbar thumbnails' cannot be patched to any version after 19.16.39")
}
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.SEEKBAR.addPreferences(

View File

@ -1,20 +1,26 @@
package app.revanced.patches.youtube.layout.seekbar
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.youtube.layout.seekbar.fingerprints.LithoLinearGradientFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.PlayerSeekbarColorFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.PlayerSeekbarGradientConfigFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.SetSeekbarClickedColorFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.ShortsSeekbarColorFingerprint
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.util.exception
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
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.instruction.TwoRegisterInstruction
@ -24,7 +30,13 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
compatiblePackages = [CompatiblePackage("com.google.android.youtube")]
)
internal object SeekbarColorBytecodePatch : BytecodePatch(
setOf(PlayerSeekbarColorFingerprint, ShortsSeekbarColorFingerprint, SetSeekbarClickedColorFingerprint)
setOf(
PlayerSeekbarColorFingerprint,
ShortsSeekbarColorFingerprint,
SetSeekbarClickedColorFingerprint,
PlayerSeekbarGradientConfigFingerprint,
LithoLinearGradientFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/theme/SeekbarColorPatch;"
@ -32,25 +44,26 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
val registerIndex = indexOfFirstWideLiteralInstructionValueOrThrow(resourceId) + 2
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
addInstructions(
registerIndex + 1,
"""
invoke-static { v$colorRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I
move-result v$colorRegister
"""
invoke-static { v$colorRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I
move-result v$colorRegister
"""
)
}
PlayerSeekbarColorFingerprint.result?.mutableMethod?.apply {
PlayerSeekbarColorFingerprint.resultOrThrow().mutableMethod.apply {
addColorChangeInstructions(SeekbarColorResourcePatch.inlineTimeBarColorizedBarPlayedColorDarkId)
addColorChangeInstructions(SeekbarColorResourcePatch.inlineTimeBarPlayedNotHighlightedColorId)
} ?: throw PlayerSeekbarColorFingerprint.exception
}
ShortsSeekbarColorFingerprint.result?.mutableMethod?.apply {
ShortsSeekbarColorFingerprint.resultOrThrow().mutableMethod.apply {
addColorChangeInstructions(SeekbarColorResourcePatch.reelTimeBarPlayedColorId)
} ?: throw ShortsSeekbarColorFingerprint.exception
}
SetSeekbarClickedColorFingerprint.result?.let { result ->
SetSeekbarClickedColorFingerprint.resultOrThrow().let { result ->
result.mutableMethod.let {
val setColorMethodIndex = result.scanResult.patternScanResult!!.startIndex + 1
val method = context
@ -69,7 +82,31 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
)
}
}
} ?: throw SetSeekbarClickedColorFingerprint.exception
}
if (VersionCheckPatch.is_19_23_or_greater) {
PlayerSeekbarGradientConfigFingerprint.resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(
PlayerSeekbarGradientConfigFingerprint.PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG
)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
addInstructions(
resultIndex + 1,
"""
invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z
move-result v$register
"""
)
}
LithoLinearGradientFingerprint.resultOrThrow().mutableMethod.apply {
addInstruction(0, "invoke-static/range { p4 .. p5 }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->setLinearGradient([I[F)V"
)
}
}
lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getLithoColor")
}

View File

@ -0,0 +1,10 @@
package app.revanced.patches.youtube.layout.seekbar.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object LithoLinearGradientFingerprint : MethodFingerprint(
accessFlags = AccessFlags.STATIC.value,
returnType = "Landroid/graphics/LinearGradient;",
parameters = listOf("F", "F", "F", "F", "[I", "[F"),
)

View File

@ -0,0 +1,15 @@
package app.revanced.patches.youtube.layout.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.seekbar.fingerprints.PlayerSeekbarGradientConfigFingerprint.PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PlayerSeekbarGradientConfigFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf(),
literalSupplier = { PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG },
) {
const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L
}

View File

@ -23,13 +23,16 @@ import app.revanced.patches.youtube.shared.fingerprints.SeekbarFingerprint
import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.exception
import app.revanced.util.alsoResolve
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
@ -41,24 +44,11 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
@ -92,12 +82,6 @@ object SponsorBlockBytecodePatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/sponsorblock/ui/SponsorBlockViewController;"
override fun execute(context: BytecodeContext) {
LayoutConstructorFingerprint.result?.let {
if (!ControlsOverlayFingerprint.resolve(context, it.classDef)) {
throw ControlsOverlayFingerprint.exception
}
} ?: throw LayoutConstructorFingerprint.exception
/*
* Hook the video time methods
*/
@ -108,67 +92,46 @@ object SponsorBlockBytecodePatch : BytecodePatch(
)
}
/*
* Set current video id.
*/
VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V")
VideoIdPatch.hookBackgroundPlayVideoId(
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V")
/*
* Seekbar drawing
*/
val seekbarSignatureResult = SeekbarFingerprint.result!!.let {
SeekbarOnDrawFingerprint.apply { resolve(context, it.mutableClass) }
}.result!!
val seekbarMethod = seekbarSignatureResult.mutableMethod
val seekbarMethodInstructions = seekbarMethod.implementation!!.instructions
/*
* Get left and right of seekbar rectangle
*/
val moveRectangleToRegisterIndex = seekbarMethodInstructions.indexOfFirst {
it.opcode == Opcode.MOVE_OBJECT_FROM16
}
// Seekbar drawing
SeekbarOnDrawFingerprint.alsoResolve(context, SeekbarFingerprint).mutableMethod.apply {
// Get left and right of seekbar rectangle.
val moveRectangleToRegisterIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_OBJECT_FROM16)
seekbarMethod.addInstruction(
moveRectangleToRegisterIndex + 1,
"invoke-static/range {p0 .. p0}, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;)V",
)
for ((index, instruction) in seekbarMethodInstructions.withIndex()) {
if (instruction.opcode != Opcode.INVOKE_STATIC) continue
val invokeInstruction = instruction as Instruction35c
if ((invokeInstruction.reference as MethodReference).name != "round") continue
val insertIndex = index + 2
// set the thickness of the segment
seekbarMethod.addInstruction(
insertIndex,
"invoke-static {v${invokeInstruction.registerC}}, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarThickness(I)V",
addInstruction(
moveRectangleToRegisterIndex + 1,
"invoke-static/range { p0 .. p0 }, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;)V",
)
break
}
/*
* Draw segment
*/
// Find the drawCircle call and draw the segment before it
for (i in seekbarMethodInstructions.size - 1 downTo 0) {
val invokeInstruction = seekbarMethodInstructions[i] as? ReferenceInstruction ?: continue
if ((invokeInstruction.reference as MethodReference).name != "drawCircle") continue
val (canvasInstance, centerY) = (invokeInstruction as FiveRegisterInstruction).let {
it.registerC to it.registerE
// Set the thickness of the segment.
val thicknessIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC && getReference<MethodReference>()?.name == "round"
}
seekbarMethod.addInstruction(
i,
"invoke-static {v$canvasInstance, v$centerY}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->drawSponsorTimeBars(Landroid/graphics/Canvas;F)V",
val thicknessRegister = getInstruction<FiveRegisterInstruction>(thicknessIndex).registerC
addInstruction(
thicknessIndex + 2,
"invoke-static { v$thicknessRegister }, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarThickness(I)V",
)
break
// Find the drawCircle call and draw the segment before it.
val drawCircleIndex = indexOfFirstInstructionReversedOrThrow {
getReference<MethodReference>()?.name == "drawCircle"
}
val drawCircleInstruction = getInstruction<FiveRegisterInstruction>(drawCircleIndex)
val canvasInstanceRegister = drawCircleInstruction.registerC
val centerYRegister = drawCircleInstruction.registerE
addInstruction(
drawCircleIndex,
"invoke-static { v$canvasInstanceRegister, v$centerYRegister }, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->" +
"drawSponsorTimeBars(Landroid/graphics/Canvas;F)V",
)
}
// Change visibility of the buttons.
@ -179,24 +142,26 @@ object SponsorBlockBytecodePatch : BytecodePatch(
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
// Append the new time to the player layout.
val appendTimeFingerprintResult = AppendTimeFingerprint.result!!
val appendTimePatternScanStartIndex = appendTimeFingerprintResult.scanResult.patternScanResult!!.startIndex
val targetRegister =
(appendTimeFingerprintResult.method.implementation!!.instructions.elementAt(appendTimePatternScanStartIndex + 1) as OneRegisterInstruction).registerA
AppendTimeFingerprint.resultOrThrow().let {
val appendTimePatternScanStartIndex = it.scanResult.patternScanResult!!.startIndex
it.mutableMethod.apply {
val register = getInstruction<OneRegisterInstruction>(appendTimePatternScanStartIndex + 1).registerA
appendTimeFingerprintResult.mutableMethod.addInstructions(
appendTimePatternScanStartIndex + 2,
"""
invoke-static {v$targetRegister}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->appendTimeWithoutSegments(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$targetRegister
""",
)
addInstructions(
appendTimePatternScanStartIndex + 2,
"""
invoke-static { v$register }, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->appendTimeWithoutSegments(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
}
// initialize the player controller
// Initialize the player controller.
VideoInformationPatch.onCreateHook(INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR, "initialize")
// initialize the sponsorblock view
ControlsOverlayFingerprint.result?.let {
// Initialize the SponsorBlock view.
ControlsOverlayFingerprint.alsoResolve(context, LayoutConstructorFingerprint).let {
val startIndex = it.scanResult.patternScanResult!!.startIndex
it.mutableMethod.apply {
val frameLayoutRegister = (getInstruction(startIndex + 2) as OneRegisterInstruction).registerA
@ -205,53 +170,50 @@ object SponsorBlockBytecodePatch : BytecodePatch(
"invoke-static {v$frameLayoutRegister}, $INTEGRATIONS_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/ViewGroup;)V",
)
}
} ?: throw ControlsOverlayFingerprint.exception
}
// get rectangle field name
RectangleFieldInvalidatorFingerprint.resolve(context, seekbarSignatureResult.classDef)
val rectangleFieldInvalidatorInstructions =
RectangleFieldInvalidatorFingerprint.result!!.method.implementation!!.instructions
val rectangleFieldName =
((rectangleFieldInvalidatorInstructions.elementAt(rectangleFieldInvalidatorInstructions.count() - 3) as ReferenceInstruction).reference as FieldReference).name
// replace the "replaceMeWith*" strings
context
.proxy(context.classes.first { it.type.endsWith("SegmentPlaybackController;") })
.mutableClass
.methods
.find { it.name == "setSponsorBarRect" }
?.let { method ->
fun MutableMethod.replaceStringInstruction(index: Int, instruction: Instruction, with: String) {
val register = (instruction as OneRegisterInstruction).registerA
this.replaceInstruction(
index,
"const-string v$register, \"$with\"",
)
}
for ((index, it) in method.implementation!!.instructions.withIndex()) {
if (it.opcode.ordinal != Opcode.CONST_STRING.ordinal) continue
// Set seekbar draw rectangle.
RectangleFieldInvalidatorFingerprint.alsoResolve(context, SeekbarOnDrawFingerprint).mutableMethod.apply {
val fieldIndex = implementation!!.instructions.count() - 3
val fieldReference = getInstruction<ReferenceInstruction>(fieldIndex).reference as FieldReference
when (((it as ReferenceInstruction).reference as StringReference).string) {
"replaceMeWithsetSponsorBarRect" -> method.replaceStringInstruction(
// replace the "replaceMeWith*" strings
context
.proxy(context.classes.first { it.type.endsWith("SegmentPlaybackController;") })
.mutableClass
.methods
.find { it.name == "setSponsorBarRect" }
?.let { method ->
fun MutableMethod.replaceStringInstruction(index: Int, instruction: Instruction, with: String) {
val register = (instruction as OneRegisterInstruction).registerA
this.replaceInstruction(
index,
it,
rectangleFieldName,
"const-string v$register, \"$with\"",
)
}
}
} ?: throw PatchException("Could not find the method which contains the replaceMeWith* strings")
for ((index, it) in method.implementation!!.instructions.withIndex()) {
if (it.opcode.ordinal != Opcode.CONST_STRING.ordinal) continue
when (((it as ReferenceInstruction).reference as StringReference).string) {
"replaceMeWithsetSponsorBarRect" -> method.replaceStringInstruction(
index,
it,
fieldReference.name,
)
}
}
} ?: throw PatchException("Could not find the method which contains the replaceMeWith* strings")
}
// The vote and create segment buttons automatically change their visibility when appropriate,
// but if buttons are showing when the end of the video is reached then they will not automatically hide.
// Add a hook to forcefully hide when the end of the video is reached.
AutoRepeatParentFingerprint.result ?: throw AutoRepeatParentFingerprint.exception
AutoRepeatFingerprint.also {
it.resolve(context, AutoRepeatParentFingerprint.result!!.classDef)
}.result?.mutableMethod?.addInstruction(
AutoRepeatFingerprint.alsoResolve(context, AutoRepeatParentFingerprint).mutableMethod.addInstruction(
0,
"invoke-static {}, $INTEGRATIONS_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR->endOfVideoReached()V",
) ?: throw AutoRepeatFingerprint.exception
)
// TODO: isSBChannelWhitelisting implementation
// TODO: isSBChannelWhitelisting implementation?
}
}

Some files were not shown because too many files have changed in this diff Show More