mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 18:03:55 +01:00
Compare commits
17 Commits
v4.4.0-dev
...
v4.5.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab2260acfd | ||
|
|
9b3c060acb | ||
|
|
bbc4a6bfa0 | ||
|
|
4df9b9d5b1 | ||
|
|
eebf72f476 | ||
|
|
a27296223f | ||
|
|
77a9a56e2d | ||
|
|
3df3441385 | ||
|
|
86def3efea | ||
|
|
bc9a612824 | ||
|
|
f378ddc6d5 | ||
|
|
7f852e8f32 | ||
|
|
5b7b134d73 | ||
|
|
df9361ea2b | ||
|
|
8c226c4bdc | ||
|
|
c87f8a376d | ||
|
|
cb4b6514db |
39
.github/workflows/sync_crowdin.yml
vendored
Normal file
39
.github/workflows/sync_crowdin.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Sync Crowdin
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 * 1 * *
|
||||
push:
|
||||
paths:
|
||||
- /src/main/resources/addresources/values/strings.xml
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync translations
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Sync translations
|
||||
uses: crowdin/github-action@v1
|
||||
with:
|
||||
config: crowdin.yml
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
localization_branch_name: feat/translations
|
||||
create_pull_request: true
|
||||
pull_request_title: "chore: Sync translations"
|
||||
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
|
||||
pull_request_base_branch_name: "dev"
|
||||
commit_message: "chore: Sync translations"
|
||||
github_user_name: revanced-bot
|
||||
github_user_email: github@revanced.app
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,3 +1,66 @@
|
||||
# [4.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.5.0-dev.1...v4.5.0-dev.2) (2024-03-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - GmsCore:** Require ignoring battery optimizations ([#2952](https://github.com/ReVanced/revanced-patches/issues/2952)) ([c0bef25](https://github.com/ReVanced/revanced-patches/commit/c0bef255909ca884838675ca6f7ac5b0e2e21730))
|
||||
|
||||
# [4.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.4.0...v4.5.0-dev.1) (2024-03-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Alternative thumbnails:** Selectively enable for home / subscription / search ([#2926](https://github.com/ReVanced/revanced-patches/issues/2926)) ([8549e1b](https://github.com/ReVanced/revanced-patches/commit/8549e1ba58ad1e1608f5e3ceacd31eeb94578949))
|
||||
|
||||
# [4.4.0](https://github.com/ReVanced/revanced-patches/compare/v4.3.0...v4.4.0) (2024-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **TikTok:** Hook application context earlier to prevent crash ([#2893](https://github.com/ReVanced/revanced-patches/issues/2893)) ([395ccda](https://github.com/ReVanced/revanced-patches/commit/395ccda7b9218c522c7ca0f99f75a12036d3e3f3))
|
||||
* **YouTube - Client spoof:** Spoof all user agents ([44a8a13](https://github.com/ReVanced/revanced-patches/commit/44a8a1399897caaff3ff45db8549ddedb2f01b68))
|
||||
* **YouTube - Downloads:** Use new task context ([#2841](https://github.com/ReVanced/revanced-patches/issues/2841)) ([6d88cb4](https://github.com/ReVanced/revanced-patches/commit/6d88cb49ec739700866290babcba5fb3032dbced))
|
||||
* **YouTube - Hide ads:** Prevent app crash if hiding fullscreen ads is not possible ([#2910](https://github.com/ReVanced/revanced-patches/issues/2910)) ([9f50470](https://github.com/ReVanced/revanced-patches/commit/9f50470bf6582fe2f20a903a97bf66c41f296fb2))
|
||||
* **YouTube Music:** Fix compatibility with latest versions ([#2924](https://github.com/ReVanced/revanced-patches/issues/2924)) ([8378c84](https://github.com/ReVanced/revanced-patches/commit/8378c8481634a63a5568480475757e64b7ec466e))
|
||||
* **YouTube:** Fix video playback by switching to ReVanced GmsCore vendor ([#2907](https://github.com/ReVanced/revanced-patches/issues/2907)) ([33ea122](https://github.com/ReVanced/revanced-patches/commit/33ea12228c2ae5dcadf3e7c7016d4bf6006d899a))
|
||||
* **YouTube:** Move setting to correct screen ([a16eda8](https://github.com/ReVanced/revanced-patches/commit/a16eda864515612d3a6b846082844df15eb49f56))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Instagram - Hide timeline ads:** Make compatible with latest versions ([a212f29](https://github.com/ReVanced/revanced-patches/commit/a212f29bd33bb5e10f024e058d26e20ee926190b))
|
||||
* **Mi Fitness:** Add `Force English locale` and `Fix login` patch ([#2734](https://github.com/ReVanced/revanced-patches/issues/2734)) ([7a25791](https://github.com/ReVanced/revanced-patches/commit/7a25791d53530b1236896b2c3d6275ee7556e8b7))
|
||||
* **Sync for Lemmy:** Add `Disable ads` patch ([#2872](https://github.com/ReVanced/revanced-patches/issues/2872)) ([0785819](https://github.com/ReVanced/revanced-patches/commit/0785819dd5ad487c778b5baf09004cdab3687184))
|
||||
* **YouTube - Downloads:** Use external downloader when selecting 'Download' in home feed flyout menu ([#2881](https://github.com/ReVanced/revanced-patches/issues/2881)) ([10afc8c](https://github.com/ReVanced/revanced-patches/commit/10afc8cc71ff29fea4937fb12fd3d1edf9c581f5))
|
||||
* **YouTube - External downloader:** Add ability to use in-app download button ([d900011](https://github.com/ReVanced/revanced-patches/commit/d9000113a905c14f8409aa75008f1ef6a1aecd0c))
|
||||
* **YouTube - Hide layout components:** Filter home/search results by keywords ([#2853](https://github.com/ReVanced/revanced-patches/issues/2853)) ([5916204](https://github.com/ReVanced/revanced-patches/commit/59162042b0a68edf7f94a3c21f838dada3c3f9c3))
|
||||
* **YouTube - Hide Shorts components:** Hide like and dislike buttons ([2df0892](https://github.com/ReVanced/revanced-patches/commit/2df0892682406e67283c4aeaacebf8f222029833))
|
||||
* **YouTube - Hide Shorts components:** Hide sound metadata label ([ea7d1e0](https://github.com/ReVanced/revanced-patches/commit/ea7d1e0d08cc245117ffe8ad0df3c31c5e87f739))
|
||||
* **YouTube - Hide Shorts components:** Hide title and full video link label ([e7b64e1](https://github.com/ReVanced/revanced-patches/commit/e7b64e154e7fb8edd0037f5e171f4aa3ed9017f8))
|
||||
* **YouTube - Hide Shorts components:** Selectively hide Shorts for home / subscription / search ([#2925](https://github.com/ReVanced/revanced-patches/issues/2925)) ([497c067](https://github.com/ReVanced/revanced-patches/commit/497c067e80e560eb125f5fc30eef9763929b4ee4))
|
||||
* **YouTube :** Remove `HDR auto brightness` patch ([#2863](https://github.com/ReVanced/revanced-patches/issues/2863)) ([b4c7bf4](https://github.com/ReVanced/revanced-patches/commit/b4c7bf48084184e5f031f7f5139a9a29341d8ebf))
|
||||
* **YouTube Vanced:** Remove `Hide ads` patch ([87887e4](https://github.com/ReVanced/revanced-patches/commit/87887e4163dd9e242209f4d0fefb415f9bc7ca75))
|
||||
* **YouTube:** Support version `19.05`, `19.06`, `19.07`, `19.08` and `19.09` ([#2862](https://github.com/ReVanced/revanced-patches/issues/2862)) ([f044dde](https://github.com/ReVanced/revanced-patches/commit/f044dde054861ff16846a6be6287f86fa3afb3d8))
|
||||
|
||||
# [4.4.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.14...v4.4.0-dev.15) (2024-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube Music:** Fix compatibility with latest versions ([#2924](https://github.com/ReVanced/revanced-patches/issues/2924)) ([8378c84](https://github.com/ReVanced/revanced-patches/commit/8378c8481634a63a5568480475757e64b7ec466e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide Shorts components:** Selectively hide Shorts for home / subscription / search ([#2925](https://github.com/ReVanced/revanced-patches/issues/2925)) ([497c067](https://github.com/ReVanced/revanced-patches/commit/497c067e80e560eb125f5fc30eef9763929b4ee4))
|
||||
|
||||
# [4.4.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.13...v4.4.0-dev.14) (2024-03-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide layout components:** Filter home/search results by keywords ([#2853](https://github.com/ReVanced/revanced-patches/issues/2853)) ([5916204](https://github.com/ReVanced/revanced-patches/commit/59162042b0a68edf7f94a3c21f838dada3c3f9c3))
|
||||
|
||||
# [4.4.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.4.0-dev.12...v4.4.0-dev.13) (2024-03-26)
|
||||
|
||||
|
||||
|
||||
@@ -302,6 +302,12 @@ public final class app/revanced/patches/moneymanager/UnlockProPatch : app/revanc
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/ad/video/HideMusicVideoAds : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/ad/video/HideMusicVideoAds;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/ad/video/MusicVideoAdsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/ad/video/MusicVideoAdsPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -314,6 +320,12 @@ public final class app/revanced/patches/music/audio/codecs/CodecsUnlockPatch : a
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -326,6 +338,12 @@ public final class app/revanced/patches/music/interaction/permanentrepeat/Perman
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -338,6 +356,12 @@ public final class app/revanced/patches/music/layout/compactheader/CompactHeader
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/layout/compactheader/HideCategoryBar : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/layout/compactheader/HideCategoryBar;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -1609,6 +1633,13 @@ public final class app/revanced/patches/youtube/misc/minimizedplayback/Minimized
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
public final fun getHookNavigationButtonCreated ()Lkotlin/jvm/functions/Function1;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch;
|
||||
public final fun addControls (Ljava/lang/String;)V
|
||||
|
||||
8
crowdin.yml
Normal file
8
crowdin.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
project_id_env: "CROWDIN_PROJECT_ID"
|
||||
api_token_env: "CROWDIN_PERSONAL_TOKEN"
|
||||
|
||||
preserve_hierarchy: false
|
||||
files:
|
||||
- source: src/main/resources/addresources/values/strings.xml
|
||||
translation: src/main/resources/addresources/values-%android_code%/strings.xml
|
||||
skip_untranslated_strings: true
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
kotlin.code.style = official
|
||||
version = 4.4.0-dev.13
|
||||
version = 4.5.0-dev.2
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package app.revanced.patches.music.ad.video
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
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.music.ad.video.fingerprints.ShowMusicVideoAdsParentFingerprint
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Hide music video ads",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object HideMusicVideoAds : BytecodePatch(
|
||||
setOf(ShowMusicVideoAdsParentFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
ShowMusicVideoAdsParentFingerprint.result?.let {
|
||||
val showMusicVideoAdsMethod = context
|
||||
.toMethodWalker(it.mutableMethod)
|
||||
.nextMethod(it.scanResult.patternScanResult!!.startIndex + 1, true).getMethod() as MutableMethod
|
||||
|
||||
showMusicVideoAdsMethod.addInstruction(0, "const/4 p1, 0x0")
|
||||
} ?: throw ShowMusicVideoAdsParentFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("This patch class has been renamed to HideMusicVideoAds.")
|
||||
object MusicVideoAdsPatch : BytecodePatch(
|
||||
dependencies = setOf(HideMusicVideoAds::class),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package app.revanced.patches.music.ad.video
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsConstructorFingerprint
|
||||
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsFingerprint
|
||||
|
||||
@Patch(
|
||||
name = "Music video ads",
|
||||
description = "Removes ads in the music player.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object MusicVideoAdsPatch : BytecodePatch(
|
||||
setOf(ShowMusicVideoAdsConstructorFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
ShowMusicVideoAdsFingerprint.resolve(context, ShowMusicVideoAdsConstructorFingerprint.result!!.classDef)
|
||||
|
||||
val result = ShowMusicVideoAdsFingerprint.result!!
|
||||
|
||||
result.mutableMethod.addInstruction(
|
||||
result.scanResult.patternScanResult!!.startIndex,
|
||||
"""
|
||||
const/4 p1, 0x0
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package app.revanced.patches.music.ad.video.fingerprints
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||
internal object ShowMusicVideoAdsConstructorFingerprint : MethodFingerprint(
|
||||
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
package app.revanced.patches.music.ad.video.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 ShowMusicVideoAdsFingerprint : MethodFingerprint(
|
||||
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("Z"), listOf(
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.music.ad.video.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object ShowMusicVideoAdsParentFingerprint : MethodFingerprint(
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET_OBJECT,
|
||||
),
|
||||
strings = listOf("maybeRegenerateCpnAndStatsClient called unexpectedly, but no error."),
|
||||
)
|
||||
@@ -10,13 +10,12 @@ import app.revanced.patches.music.audio.codecs.fingerprints.CodecsLockFingerprin
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Patch(
|
||||
name = "Codecs unlock",
|
||||
description = "Adds more audio codec options. The new audio codecs usually result in better audio quality.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
@Deprecated("This patch is no longer needed as the feature is now enabled by default.")
|
||||
object CodecsUnlockPatch : BytecodePatch(
|
||||
setOf(CodecsLockFingerprint, AllCodecsReferenceFingerprint)
|
||||
setOf(CodecsLockFingerprint, AllCodecsReferenceFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val codecsLockResult = CodecsLockFingerprint.result!!
|
||||
@@ -25,13 +24,13 @@ object CodecsUnlockPatch : BytecodePatch(
|
||||
|
||||
val scanResultStartIndex = codecsLockResult.scanResult.patternScanResult!!.startIndex
|
||||
val instructionIndex = scanResultStartIndex +
|
||||
if (implementation.instructions[scanResultStartIndex - 1].opcode == Opcode.CHECK_CAST) {
|
||||
// for 5.16.xx and lower
|
||||
-3
|
||||
} else {
|
||||
// since 5.17.xx
|
||||
-2
|
||||
}
|
||||
if (implementation.instructions[scanResultStartIndex - 1].opcode == Opcode.CHECK_CAST) {
|
||||
// for 5.16.xx and lower
|
||||
-3
|
||||
} else {
|
||||
// since 5.17.xx
|
||||
-2
|
||||
}
|
||||
|
||||
val allCodecsResult = AllCodecsReferenceFingerprint.result!!
|
||||
val allCodecsMethod =
|
||||
@@ -41,7 +40,7 @@ object CodecsUnlockPatch : BytecodePatch(
|
||||
|
||||
implementation.replaceInstruction(
|
||||
instructionIndex,
|
||||
"invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;".toInstruction()
|
||||
"invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;".toInstruction(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
package app.revanced.patches.music.audio.exclusiveaudio
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.audio.exclusiveaudio.fingerprints.AllowExclusiveAudioPlaybackFingerprint
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Exclusive audio playback",
|
||||
name = "Enable exclusive audio playback",
|
||||
description = "Enables the option to play audio without video.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object ExclusiveAudioPatch : BytecodePatch(
|
||||
setOf(AllowExclusiveAudioPlaybackFingerprint)
|
||||
object EnableExclusiveAudioPlayback : BytecodePatch(
|
||||
setOf(AllowExclusiveAudioPlaybackFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AllowExclusiveAudioPlaybackFingerprint.result?.mutableMethod?.apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
"""
|
||||
""",
|
||||
)
|
||||
} ?: throw AllowExclusiveAudioPlaybackFingerprint.exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("This patch class has been renamed to EnableExclusiveAudioPlayback.")
|
||||
object ExclusiveAudioPatch : BytecodePatch(emptySet()) {
|
||||
override fun execute(context: BytecodeContext) = EnableExclusiveAudioPlayback.execute(context)
|
||||
}
|
||||
@@ -1,25 +1,32 @@
|
||||
package app.revanced.patches.music.interaction.permanentshuffle
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.interaction.permanentshuffle.fingerprints.DisableShuffleFingerprint
|
||||
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Permanent shuffle",
|
||||
description = "Permanently remember your shuffle preference " +
|
||||
"even if the playlist ends or another track is played.",
|
||||
"even if the playlist ends or another track is played.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
use = false
|
||||
use = false,
|
||||
)
|
||||
@Suppress("unused")
|
||||
object PermanentShuffleTogglePatch : BytecodePatch(setOf(DisableShuffleFingerprint)) {
|
||||
object PermanentShufflePatch : BytecodePatch(setOf(DisableShuffleFingerprint)) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
DisableShuffleFingerprint.result?.mutableMethod?.addInstruction(0, "return-void")
|
||||
?: throw DisableShuffleFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("This patch class has been renamed to PermanentShufflePatch.")
|
||||
object PermanentShuffleTogglePatch : BytecodePatch(
|
||||
dependencies = setOf(PermanentShufflePatch::class),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package app.revanced.patches.music.layout.compactheader
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.layout.compactheader.fingerprints.CompactHeaderConstructorFingerprint
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
|
||||
|
||||
@Patch(
|
||||
name = "Compact header",
|
||||
description = "Hides the music category bar at the top of the homepage.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
use = false
|
||||
)
|
||||
@Suppress("unused")
|
||||
object CompactHeaderPatch : BytecodePatch(
|
||||
setOf(CompactHeaderConstructorFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val result = CompactHeaderConstructorFingerprint.result!!
|
||||
val method = result.mutableMethod
|
||||
|
||||
val insertIndex = result.scanResult.patternScanResult!!.endIndex
|
||||
val register = (method.implementation!!.instructions[insertIndex - 1] as BuilderInstruction11x).registerA
|
||||
method.addInstructions(
|
||||
insertIndex, """
|
||||
const/16 v2, 0x8
|
||||
invoke-virtual {v${register}, v2}, Landroid/view/View;->setVisibility(I)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package app.revanced.patches.music.layout.compactheader
|
||||
|
||||
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.music.layout.compactheader.fingerprints.ConstructCategoryBarFingerprint
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Hide category bar",
|
||||
description = "Hides the category bar at the top of the homepage.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
use = false,
|
||||
)
|
||||
@Suppress("unused")
|
||||
object HideCategoryBar : BytecodePatch(
|
||||
setOf(ConstructCategoryBarFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
ConstructCategoryBarFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
const/16 v2, 0x8
|
||||
invoke-virtual {v$register, v2}, Landroid/view/View;->setVisibility(I)V
|
||||
""",
|
||||
)
|
||||
}
|
||||
} ?: throw ConstructCategoryBarFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("This patch class has been renamed to HideCategoryBar.")
|
||||
object CompactHeaderPatch : BytecodePatch(
|
||||
dependencies = setOf(HideCategoryBar::class),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,19 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object CompactHeaderConstructorFingerprint : MethodFingerprint(
|
||||
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L", "L", "L"), listOf(
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
internal object ConstructCategoryBarFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
listOf("Landroid/content/Context;", "L", "L", "L"),
|
||||
listOf(
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.CONST,
|
||||
Opcode.CONST_4,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST
|
||||
)
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.CONST,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
),
|
||||
)
|
||||
@@ -1,26 +1,39 @@
|
||||
package app.revanced.patches.music.layout.minimizedplayback
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
|
||||
|
||||
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.BackgroundPlaybackDisableFingerprint
|
||||
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Minimized playback music",
|
||||
description = "Enables minimized playback on Kids music.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
|
||||
name = "Minimized playback",
|
||||
description = "Unlocks options for picture-in-picture and background playback.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object MinimizedPlaybackPatch : BytecodePatch(setOf(MinimizedPlaybackManagerFingerprint)) {
|
||||
override fun execute(context: BytecodeContext) =
|
||||
MinimizedPlaybackManagerFingerprint.result?.mutableMethod?.addInstruction(
|
||||
object MinimizedPlaybackPatch : BytecodePatch(
|
||||
setOf(
|
||||
KidsMinimizedPlaybackPolicyControllerFingerprint,
|
||||
BackgroundPlaybackDisableFingerprint,
|
||||
),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
KidsMinimizedPlaybackPolicyControllerFingerprint.result?.mutableMethod?.addInstruction(
|
||||
0,
|
||||
"return-void",
|
||||
) ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception
|
||||
|
||||
BackgroundPlaybackDisableFingerprint.result?.mutableMethod?.addInstructions(
|
||||
0,
|
||||
"""
|
||||
return-void
|
||||
"""
|
||||
) ?: throw MinimizedPlaybackManagerFingerprint.exception
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
""",
|
||||
) ?: throw BackgroundPlaybackDisableFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package app.revanced.patches.music.layout.minimizedplayback.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 BackgroundPlaybackDisableFingerprint : MethodFingerprint(
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
listOf("L"),
|
||||
listOf(
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET,
|
||||
Opcode.AND_INT_LIT16,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IGET,
|
||||
),
|
||||
)
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
|
||||
internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
listOf("I", "L", "Z"),
|
||||
@@ -22,5 +22,5 @@ internal object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_NE,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -2,41 +2,44 @@ package app.revanced.patches.music.layout.premium
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumFingerprint
|
||||
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumParentFingerprint
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Hide get premium",
|
||||
description = "Removes all \"Get Premium\" evidences from the avatar menu.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
|
||||
name = "Hide 'Get Music Premium' label",
|
||||
description = "Hides the red \"Get Music Premium\" label from the account menu.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object HideGetPremiumPatch : BytecodePatch(setOf(HideGetPremiumParentFingerprint)) {
|
||||
object HideGetPremiumPatch : BytecodePatch(
|
||||
setOf(HideGetPremiumFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val parentResult = HideGetPremiumParentFingerprint.result!!
|
||||
HideGetPremiumFingerprint.resolve(context, parentResult.classDef)
|
||||
HideGetPremiumFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
|
||||
val startIndex = parentResult.scanResult.patternScanResult!!.startIndex
|
||||
val setVisibilityInstruction = getInstruction<FiveRegisterInstruction>(insertIndex)
|
||||
val getPremiumViewRegister = setVisibilityInstruction.registerC
|
||||
val visibilityRegister = setVisibilityInstruction.registerD
|
||||
|
||||
val parentMethod = parentResult.mutableMethod
|
||||
parentMethod.replaceInstruction(
|
||||
startIndex,
|
||||
"""
|
||||
const/4 v1, 0x0
|
||||
"""
|
||||
)
|
||||
replaceInstruction(
|
||||
insertIndex,
|
||||
"const/16 v$visibilityRegister, 0x8",
|
||||
)
|
||||
|
||||
val result = HideGetPremiumFingerprint.result!!
|
||||
val method = result.mutableMethod
|
||||
method.addInstruction(
|
||||
startIndex,
|
||||
"""
|
||||
const/16 v0, 0x8
|
||||
"""
|
||||
)
|
||||
addInstruction(
|
||||
insertIndex + 1,
|
||||
"invoke-virtual {v$getPremiumViewRegister, v$visibilityRegister}, " +
|
||||
"Landroid/view/View;->setVisibility(I)V",
|
||||
)
|
||||
}
|
||||
} ?: throw HideGetPremiumFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object HideGetPremiumFingerprint : MethodFingerprint(
|
||||
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
listOf(),
|
||||
listOf(
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.CONST_16,
|
||||
Opcode.GOTO,
|
||||
Opcode.NOP,
|
||||
Opcode.INVOKE_VIRTUAL
|
||||
)
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
),
|
||||
listOf("FEmusic_history", "FEmusic_offline"),
|
||||
)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package app.revanced.patches.music.layout.premium.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 HideGetPremiumParentFingerprint : MethodFingerprint(
|
||||
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
||||
Opcode.IGET_BOOLEAN,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_STATIC
|
||||
),
|
||||
listOf("FEmusic_history"),
|
||||
)
|
||||
@@ -1,71 +1,84 @@
|
||||
package app.revanced.patches.music.layout.upgradebutton
|
||||
|
||||
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.getInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.extensions.newLabel
|
||||
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.smali.toInstructions
|
||||
import app.revanced.patches.music.layout.upgradebutton.fingerprints.PivotBarConstructorFingerprint
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
|
||||
@Patch(
|
||||
name = "Remove upgrade button",
|
||||
description = "Removes the upgrade tab from the pivot bar.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object RemoveUpgradeButtonPatch : BytecodePatch(
|
||||
setOf(PivotBarConstructorFingerprint)
|
||||
setOf(PivotBarConstructorFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val result = PivotBarConstructorFingerprint.result!!
|
||||
val implementation = result.mutableMethod.implementation!!
|
||||
PivotBarConstructorFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val pivotBarElementFieldReference = getInstruction(it.scanResult.patternScanResult!!.endIndex - 1)
|
||||
.getReference<FieldReference>()
|
||||
|
||||
val pivotBarElementFieldRef =
|
||||
(implementation.instructions[result.scanResult.patternScanResult!!.endIndex - 1] as Instruction22c).reference
|
||||
val register = (getInstructions().first() as Instruction35c).registerC
|
||||
|
||||
val register = (implementation.instructions.first() as Instruction35c).registerC
|
||||
// first compile all the needed instructions
|
||||
val instructionList = """
|
||||
invoke-interface { v0 }, Ljava/util/List;->size()I
|
||||
move-result v1
|
||||
const/4 v2, 0x4
|
||||
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
||||
iput-object v0, v$register, $pivotBarElementFieldRef
|
||||
""".toInstructions().toMutableList()
|
||||
// First compile all the needed instructions.
|
||||
val instructionList = """
|
||||
invoke-interface { v0 }, Ljava/util/List;->size()I
|
||||
move-result v1
|
||||
const/4 v2, 0x4
|
||||
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
||||
iput-object v0, v$register, $pivotBarElementFieldReference
|
||||
""".toInstructions().toMutableList()
|
||||
|
||||
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
|
||||
val endIndex = result.scanResult.patternScanResult!!.endIndex
|
||||
// Replace the instruction to retain the label at given index.
|
||||
replaceInstruction(
|
||||
endIndex - 1,
|
||||
instructionList[0], // invoke-interface.
|
||||
)
|
||||
// Do not forget to remove this instruction since we added it already.
|
||||
instructionList.removeFirst()
|
||||
|
||||
// replace the instruction to retain the label at given index
|
||||
implementation.replaceInstruction(
|
||||
endIndex - 1, instructionList[0] // invoke-interface
|
||||
)
|
||||
// do not forget to remove this instruction since we added it already
|
||||
instructionList.removeFirst()
|
||||
val exitInstruction = instructionList.last() // iput-object
|
||||
addInstruction(
|
||||
endIndex,
|
||||
exitInstruction,
|
||||
)
|
||||
// Do not forget to remove this instruction since we added it already.
|
||||
instructionList.removeLast()
|
||||
|
||||
val exitInstruction = instructionList.last() // iput-object
|
||||
implementation.addInstruction(
|
||||
endIndex, exitInstruction
|
||||
)
|
||||
// do not forget to remove this instruction since we added it already
|
||||
instructionList.removeLast()
|
||||
// Add the necessary if statement to remove the upgrade tab button in case it exists.
|
||||
instructionList.add(
|
||||
2, // if-le.
|
||||
BuilderInstruction22t(
|
||||
Opcode.IF_LE,
|
||||
1,
|
||||
2,
|
||||
newLabel(endIndex),
|
||||
),
|
||||
)
|
||||
|
||||
// add the necessary if statement to remove the upgrade tab button in case it exists
|
||||
instructionList.add(
|
||||
2, // if-le
|
||||
BuilderInstruction22t(
|
||||
Opcode.IF_LE, 1, 2, implementation.newLabelForIndex(endIndex)
|
||||
)
|
||||
)
|
||||
|
||||
implementation.addInstructions(
|
||||
endIndex, instructionList
|
||||
)
|
||||
addInstructions(
|
||||
endIndex,
|
||||
instructionList,
|
||||
)
|
||||
}
|
||||
} ?: throw PivotBarConstructorFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,19 @@
|
||||
package app.revanced.patches.music.layout.upgradebutton.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
|
||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||
internal object PivotBarConstructorFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
listOf("L", "Z"),
|
||||
listOf(
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.GOTO,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IGET,
|
||||
Opcode.CONST,
|
||||
Opcode.IF_NE,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.GOTO,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IGET,
|
||||
Opcode.CONST,
|
||||
Opcode.IF_NE,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.GOTO,
|
||||
Opcode.NOP,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
)
|
||||
Opcode.RETURN_VOID,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -34,5 +34,5 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
||||
PrimeMethodFingerprint,
|
||||
),
|
||||
) {
|
||||
override val gmsCoreVendor by gmsCoreVendorGroupIdOption
|
||||
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
}
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
package app.revanced.patches.music.premium.backgroundplay
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.premium.backgroundplay.fingerprints.BackgroundPlaybackDisableFingerprint
|
||||
|
||||
|
||||
@Patch(
|
||||
name = "Background play",
|
||||
description = "Enables playing music in the background.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object BackgroundPlayPatch : BytecodePatch(setOf(BackgroundPlaybackDisableFingerprint)) {
|
||||
override fun execute(context: BytecodeContext) =
|
||||
BackgroundPlaybackDisableFingerprint.result?.mutableMethod?.addInstructions(
|
||||
0, """
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
"""
|
||||
) ?: throw BackgroundPlaybackDisableFingerprint.exception
|
||||
}
|
||||
import app.revanced.patches.music.layout.minimizedplayback.MinimizedPlaybackPatch
|
||||
@Deprecated("This patch has been merged into MinimizedPlaybackPatch.")
|
||||
object BackgroundPlayPatch : BytecodePatch(
|
||||
dependencies = setOf(MinimizedPlaybackPatch::class),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package app.revanced.patches.music.premium.backgroundplay.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
|
||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||
internal object BackgroundPlaybackDisableFingerprint : MethodFingerprint(
|
||||
"Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), listOf(
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET,
|
||||
Opcode.AND_INT_LIT16,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IGET,
|
||||
Opcode.CONST,
|
||||
Opcode.IF_NE,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IGET,
|
||||
Opcode.IF_NE,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.GOTO,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.GOTO,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_BOOLEAN,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.CONST_4,
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURN
|
||||
)
|
||||
)
|
||||
@@ -2,7 +2,7 @@ package app.revanced.patches.shared.misc.gms
|
||||
|
||||
import app.revanced.patcher.PatchClass
|
||||
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.getInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
@@ -12,7 +12,7 @@ import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.AC
|
||||
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.GmsCoreSupportFingerprint
|
||||
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_METHOD_NAME
|
||||
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.returnEarly
|
||||
@@ -32,7 +32,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
|
||||
* @param primeMethodFingerprint The fingerprint of the "prime" method that needs to be patched.
|
||||
* @param earlyReturnFingerprints The fingerprints of methods that need to be returned early.
|
||||
* @param mainActivityOnCreateFingerprint The fingerprint of the main activity's onCreate method.
|
||||
* @param mainActivityOnCreateFingerprint The fingerprint of the main activity onCreate method.
|
||||
* @param integrationsPatchDependency The patch responsible for the integrations.
|
||||
* @param gmsCoreSupportResourcePatch The corresponding resource patch that is used to patch the resources.
|
||||
* @param dependencies Additional dependencies of this patch.
|
||||
@@ -60,7 +60,10 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
integrationsPatchDependency,
|
||||
) + dependencies,
|
||||
compatiblePackages = compatiblePackages,
|
||||
fingerprints = setOf(GmsCoreSupportFingerprint, mainActivityOnCreateFingerprint) + fingerprints,
|
||||
fingerprints = setOf(
|
||||
GmsCoreSupportFingerprint,
|
||||
mainActivityOnCreateFingerprint,
|
||||
) + fingerprints,
|
||||
requiresIntegrations = true,
|
||||
) {
|
||||
init {
|
||||
@@ -68,7 +71,7 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
gmsCoreSupportResourcePatch.options.values.forEach(options::register)
|
||||
}
|
||||
|
||||
internal abstract val gmsCoreVendor: String?
|
||||
internal abstract val gmsCoreVendorGroupId: String?
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
|
||||
@@ -93,16 +96,17 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
// Return these methods early to prevent the app from crashing.
|
||||
earlyReturnFingerprints.toList().returnEarly()
|
||||
|
||||
// Check the availability of GmsCore.
|
||||
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstruction(
|
||||
1, // Hack to not disturb other patches (such as the integrations patch).
|
||||
"invoke-static {}, Lapp/revanced/integrations/youtube/patches/GmsCoreSupport;->checkAvailability()V",
|
||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions(
|
||||
1, // Hack to not disturb other patches (such as the YTMusic integrations patch).
|
||||
"invoke-static/range { p0 .. p0 }, Lapp/revanced/integrations/shared/GmsCoreSupport;->" +
|
||||
"checkGmsCore(Landroid/content/Context;)V",
|
||||
) ?: throw mainActivityOnCreateFingerprint.exception
|
||||
|
||||
// Change the vendor of GmsCore in ReVanced Integrations.
|
||||
GmsCoreSupportFingerprint.result?.mutableClass?.methods
|
||||
?.single { it.name == GET_GMS_CORE_VENDOR_METHOD_NAME }
|
||||
?.replaceInstruction(0, "const-string v0, \"$gmsCoreVendor\"")
|
||||
?.single { it.name == GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME }
|
||||
?.replaceInstruction(0, "const-string v0, \"$gmsCoreVendorGroupId\"")
|
||||
?: throw GmsCoreSupportFingerprint.exception
|
||||
}
|
||||
|
||||
@@ -146,10 +150,10 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
in PERMISSIONS,
|
||||
in ACTIONS,
|
||||
in AUTHORITIES,
|
||||
-> referencedString.replace("com.google", gmsCoreVendor!!)
|
||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
||||
|
||||
// No vendor prefix for whatever reason...
|
||||
"subscribedfeeds" -> "$gmsCoreVendor.subscribedfeeds"
|
||||
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -162,7 +166,7 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
if (str.startsWith(uriPrefix)) {
|
||||
return str.replace(
|
||||
uriPrefix,
|
||||
"content://${authority.replace("com.google", gmsCoreVendor!!)}",
|
||||
"content://${authority.replace("com.google", gmsCoreVendorGroupId!!)}",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -170,7 +174,7 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
// gms also has a 'subscribedfeeds' authority, check for that one too
|
||||
val subFeedsUriPrefix = "content://subscribedfeeds"
|
||||
if (str.startsWith(subFeedsUriPrefix)) {
|
||||
return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendor.subscribedfeeds")
|
||||
return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendorGroupId.subscribedfeeds")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,6 @@ abstract class BaseGmsCoreSupportResourcePatch(
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val VANCED_VENDOR = "com.mgoogle"
|
||||
private const val PACKAGE_NAME_REGEX_PATTERN = "^[a-z]\\w*(\\.[a-z]\\w*)+\$"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
internal object GmsCoreSupportFingerprint : MethodFingerprint(
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.type.endsWith("GmsCoreSupport;")
|
||||
}
|
||||
},
|
||||
) {
|
||||
const val GET_GMS_CORE_VENDOR_METHOD_NAME = "getGmsCoreVendor"
|
||||
const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId"
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import app.revanced.util.resultOrThrow
|
||||
|
||||
@Patch(
|
||||
name = "Downloads",
|
||||
description = "Adds support to download videos with an external downloader app" +
|
||||
description = "Adds support to download videos with an external downloader app " +
|
||||
"using the in-app download button or a video player action button.",
|
||||
dependencies = [
|
||||
DownloadsResourcePatch::class,
|
||||
|
||||
@@ -10,10 +10,10 @@ import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.*
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.REGISTER_TEMPLATE_REPLACEMENT
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.injectHook
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.ANDROID_AUTOMOTIVE_STRING
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.AddCreateButtonViewFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
@@ -24,8 +24,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
dependencies = [
|
||||
IntegrationsPatch::class,
|
||||
SettingsPatch::class,
|
||||
ResolvePivotBarFingerprintsPatch::class,
|
||||
AddResourcesPatch::class,
|
||||
NavigationBarHookPatch::class,
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
@@ -47,7 +47,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"19.06.39",
|
||||
"19.07.40",
|
||||
"19.08.36",
|
||||
"19.09.37"
|
||||
"19.09.37",
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -76,53 +76,7 @@ object NavigationButtonsPatch : BytecodePatch(
|
||||
),
|
||||
)
|
||||
|
||||
/*
|
||||
* Resolve fingerprints
|
||||
*/
|
||||
|
||||
val initializeButtonsResult = InitializeButtonsFingerprint.result!!
|
||||
|
||||
val fingerprintResults =
|
||||
arrayOf(PivotBarEnumFingerprint, PivotBarButtonsViewFingerprint)
|
||||
.onEach {
|
||||
if (!it.resolve(
|
||||
context,
|
||||
initializeButtonsResult.mutableMethod,
|
||||
initializeButtonsResult.mutableClass,
|
||||
)
|
||||
) {
|
||||
throw it.exception
|
||||
}
|
||||
}
|
||||
.map { it.result!!.scanResult.patternScanResult!! }
|
||||
|
||||
val enumScanResult = fingerprintResults[0]
|
||||
val buttonViewResult = fingerprintResults[1]
|
||||
|
||||
val enumHookInsertIndex = enumScanResult.startIndex + 2
|
||||
val buttonHookInsertIndex = buttonViewResult.endIndex
|
||||
|
||||
/*
|
||||
* Inject hooks
|
||||
*/
|
||||
|
||||
val enumHook = "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, " +
|
||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->lastNavigationButton:Ljava/lang/Enum;"
|
||||
val buttonHook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " +
|
||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->hideButton(Landroid/view/View;)V"
|
||||
|
||||
// Inject bottom to top to not mess up the indices
|
||||
mapOf(
|
||||
buttonHook to buttonHookInsertIndex,
|
||||
enumHook to enumHookInsertIndex,
|
||||
).forEach { (hook, insertIndex) ->
|
||||
initializeButtonsResult.mutableMethod.injectHook(hook, insertIndex)
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide create or switch it with notifications buttons.
|
||||
*/
|
||||
|
||||
// Switch create with notifications button.
|
||||
AddCreateButtonViewFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val stringIndex = it.scanResult.stringsScanResult!!.matches.find { match ->
|
||||
@@ -130,7 +84,8 @@ object NavigationButtonsPatch : BytecodePatch(
|
||||
}!!.index
|
||||
|
||||
val conditionalCheckIndex = stringIndex - 1
|
||||
val conditionRegister = getInstruction<OneRegisterInstruction>(conditionalCheckIndex).registerA
|
||||
val conditionRegister =
|
||||
getInstruction<OneRegisterInstruction>(conditionalCheckIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
conditionalCheckIndex,
|
||||
@@ -142,26 +97,7 @@ object NavigationButtonsPatch : BytecodePatch(
|
||||
}
|
||||
} ?: throw AddCreateButtonViewFingerprint.exception
|
||||
|
||||
/*
|
||||
* Resolve fingerprints
|
||||
*/
|
||||
|
||||
InitializeButtonsFingerprint.result!!.let {
|
||||
if (!PivotBarCreateButtonViewFingerprint.resolve(context, it.mutableMethod, it.mutableClass)) {
|
||||
throw PivotBarCreateButtonViewFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
PivotBarCreateButtonViewFingerprint.result!!.apply {
|
||||
val insertIndex = scanResult.patternScanResult!!.endIndex
|
||||
|
||||
/*
|
||||
* Inject hooks
|
||||
*/
|
||||
val hook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " +
|
||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->hideCreateButton(Landroid/view/View;)V"
|
||||
|
||||
mutableMethod.injectHook(hook, insertIndex)
|
||||
}
|
||||
// Hook navigation button created, in order to hide them.
|
||||
NavigationBarHookPatch.hookNavigationButtonCreated(INTEGRATIONS_CLASS_DESCRIPTOR)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.buttons.navigation
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.InitializeButtonsFingerprint
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.PivotBarConstructorFingerprint
|
||||
|
||||
@Patch(
|
||||
description = "Resolves necessary fingerprints.",
|
||||
dependencies = [ResourceMappingPatch::class]
|
||||
)
|
||||
internal object ResolvePivotBarFingerprintsPatch : BytecodePatch(
|
||||
setOf(PivotBarConstructorFingerprint)
|
||||
) {
|
||||
internal var imageOnlyTabResourceId: Long = -1
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// imageOnlyTabResourceId is used in InitializeButtonsFingerprint fingerprint
|
||||
ResourceMappingPatch.resourceMappings.find { it.type == "layout" && it.name == "image_only_tab" }
|
||||
?.let { imageOnlyTabResourceId = it.id } ?: throw PatchException("Failed to find resource")
|
||||
|
||||
PivotBarConstructorFingerprint.result?.let {
|
||||
// Resolve InitializeButtonsFingerprint on the class of the method
|
||||
// which PivotBarConstructorFingerprint resolved to
|
||||
if (!InitializeButtonsFingerprint.resolve(
|
||||
context,
|
||||
it.classDef
|
||||
)
|
||||
) throw InitializeButtonsFingerprint.exception
|
||||
} ?: throw PivotBarConstructorFingerprint.exception
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object PivotBarButtonsViewFingerprint : MethodFingerprint(
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_VIRTUAL_RANGE,
|
||||
Opcode.MOVE_RESULT_OBJECT, // target reference
|
||||
Opcode.GOTO,
|
||||
)
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object PivotBarCreateButtonViewFingerprint : MethodFingerprint(
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_DIRECT_RANGE,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_STATIC
|
||||
)
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object PivotBarEnumFingerprint : MethodFingerprint(
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IF_NEZ, // target reference
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
)
|
||||
)
|
||||
@@ -1,30 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.buttons.navigation.utils
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import com.android.tools.smali.dexlib2.Opcode.MOVE_RESULT_OBJECT
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
internal object InjectionUtils {
|
||||
const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX"
|
||||
|
||||
/**
|
||||
* Injects an instruction into insertIndex of the hook.
|
||||
* @param hook The hook to insert.
|
||||
* @param insertIndex The index to insert the instruction at.
|
||||
* [MOVE_RESULT_OBJECT] has to be the previous instruction before [insertIndex].
|
||||
*/
|
||||
fun MutableMethod.injectHook(hook: String, insertIndex: Int) {
|
||||
val injectTarget = this
|
||||
|
||||
// Register to pass to the hook
|
||||
val registerIndex = insertIndex - 1 // MOVE_RESULT_OBJECT is always the previous instruction
|
||||
val register = injectTarget.getInstruction<OneRegisterInstruction>(registerIndex).registerA
|
||||
|
||||
injectTarget.addInstruction(
|
||||
insertIndex,
|
||||
hook.replace("REGISTER_INDEX", register.toString()),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
@@ -20,8 +21,10 @@ import app.revanced.patches.youtube.layout.hide.general.fingerprints.ParseElemen
|
||||
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.misc.litho.filter.LithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.util.exception
|
||||
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.TwoRegisterInstruction
|
||||
@@ -33,6 +36,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
LithoFilterPatch::class,
|
||||
SettingsPatch::class,
|
||||
AddResourcesPatch::class,
|
||||
NavigationBarHookPatch::class,
|
||||
PlayerTypeHookPatch::class // Used by Keyword Content filter.
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
@@ -69,6 +74,8 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
"Lapp/revanced/integrations/youtube/patches/components/DescriptionComponentsFilter;"
|
||||
private const val CUSTOM_FILTER_CLASS_NAME =
|
||||
"Lapp/revanced/integrations/youtube/patches/components/CustomFilter;"
|
||||
private const val KEYWORD_FILTER_CLASS_NAME =
|
||||
"Lapp/revanced/integrations/youtube/patches/components/KeywordContentFilter;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(this::class)
|
||||
@@ -117,6 +124,20 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
SwitchPreference("revanced_hide_search_result_shelf_header"),
|
||||
)
|
||||
|
||||
SettingsPatch.PreferenceScreen.FEED.addPreferences(
|
||||
PreferenceScreen(
|
||||
key = "revanced_hide_keyword_content_screen",
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_hide_keyword_content_home"),
|
||||
SwitchPreference("revanced_hide_keyword_content_subscriptions"),
|
||||
SwitchPreference("revanced_hide_keyword_content_search"),
|
||||
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
|
||||
NonInteractivePreference("revanced_hide_keyword_content_about")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
SwitchPreference("revanced_hide_gray_separator"),
|
||||
PreferenceScreen(
|
||||
@@ -136,19 +157,19 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
|
||||
LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
|
||||
LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME)
|
||||
LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME)
|
||||
LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_NAME)
|
||||
|
||||
// region Mix playlists
|
||||
|
||||
ParseElementFromBufferFingerprint.result?.let { result ->
|
||||
val returnEmptyComponentInstruction =
|
||||
result.mutableMethod.getInstructions().last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
ParseElementFromBufferFingerprint.resultOrThrow().let { result ->
|
||||
val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
result.mutableMethod.apply {
|
||||
val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex
|
||||
val conversionContextRegister =
|
||||
getInstruction<TwoRegisterInstruction>(consumeByteBufferIndex - 2).registerA
|
||||
val byteBufferRegister = getInstruction<FiveRegisterInstruction>(consumeByteBufferIndex).registerD
|
||||
val returnEmptyComponentInstruction = getInstructions().last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
|
||||
addInstructionsWithLabels(
|
||||
consumeByteBufferIndex,
|
||||
@@ -160,15 +181,15 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
|
||||
)
|
||||
}
|
||||
} ?: throw ParseElementFromBufferFingerprint.exception
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Watermark (legacy code for old versions of YouTube)
|
||||
|
||||
ShowWatermarkFingerprint.also {
|
||||
it.resolve(context, PlayerOverlayFingerprint.result?.classDef ?: throw PlayerOverlayFingerprint.exception)
|
||||
}.result?.mutableMethod?.apply {
|
||||
it.resolve(context, PlayerOverlayFingerprint.resultOrThrow().classDef)
|
||||
}.resultOrThrow().mutableMethod.apply {
|
||||
val index = implementation!!.instructions.size - 5
|
||||
|
||||
removeInstruction(index)
|
||||
@@ -179,7 +200,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
move-result p2
|
||||
""",
|
||||
)
|
||||
} ?: throw ShowWatermarkFingerprint.exception
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
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.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||
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
|
||||
@@ -25,7 +27,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
IntegrationsPatch::class,
|
||||
LithoFilterPatch::class,
|
||||
HideShortsComponentsResourcePatch::class,
|
||||
ResourceMappingPatch::class
|
||||
ResourceMappingPatch::class,
|
||||
NavigationBarHookPatch::class,
|
||||
PlayerTypeHookPatch::class
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
|
||||
@@ -17,22 +17,32 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
|
||||
AddResourcesPatch(this::class)
|
||||
|
||||
SettingsPatch.PreferenceScreen.SHORTS.addPreferences(
|
||||
SwitchPreference("revanced_hide_shorts"),
|
||||
SwitchPreference("revanced_hide_shorts_join_button"),
|
||||
SwitchPreference("revanced_hide_shorts_subscribe_button"),
|
||||
SwitchPreference("revanced_hide_shorts_subscribe_button_paused"),
|
||||
SwitchPreference("revanced_hide_shorts_thanks_button"),
|
||||
SwitchPreference("revanced_hide_shorts_home"),
|
||||
SwitchPreference("revanced_hide_shorts_subscriptions"),
|
||||
SwitchPreference("revanced_hide_shorts_search"),
|
||||
|
||||
// Shorts player components.
|
||||
// Ideally each group should be ordered similar to how they appear in the UI
|
||||
// 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_button"),
|
||||
SwitchPreference("revanced_hide_shorts_dislike_button"),
|
||||
SwitchPreference("revanced_hide_shorts_comments_button"),
|
||||
SwitchPreference("revanced_hide_shorts_remix_button"),
|
||||
SwitchPreference("revanced_hide_shorts_share_button"),
|
||||
SwitchPreference("revanced_hide_shorts_info_panel"),
|
||||
SwitchPreference("revanced_hide_shorts_remix_button"),
|
||||
SwitchPreference("revanced_hide_shorts_sound_button"),
|
||||
|
||||
// Everything else.
|
||||
SwitchPreference("revanced_hide_shorts_thanks_button"),
|
||||
SwitchPreference("revanced_hide_shorts_join_button"),
|
||||
SwitchPreference("revanced_hide_shorts_subscribe_button"),
|
||||
SwitchPreference("revanced_hide_shorts_subscribe_button_paused"),
|
||||
SwitchPreference("revanced_hide_shorts_channel_bar"),
|
||||
SwitchPreference("revanced_hide_shorts_info_panel"),
|
||||
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
|
||||
SwitchPreference("revanced_hide_shorts_video_title"),
|
||||
SwitchPreference("revanced_hide_shorts_sound_metadata_label"),
|
||||
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
|
||||
SwitchPreference("revanced_hide_shorts_sound_button"),
|
||||
SwitchPreference("revanced_hide_shorts_navigation_bar"),
|
||||
)
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.reques
|
||||
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
|
||||
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnSucceededFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
@@ -38,6 +40,8 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
IntegrationsPatch::class,
|
||||
SettingsPatch::class,
|
||||
AddResourcesPatch::class,
|
||||
NavigationBarHookPatch::class,
|
||||
PlayerTypeHookPatch::class
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
@@ -127,25 +131,45 @@ object AlternativeThumbnailsPatch : BytecodePatch(
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(this::class)
|
||||
|
||||
val entries = "revanced_alt_thumbnail_options_entries"
|
||||
val values = "revanced_alt_thumbnail_options_entry_values"
|
||||
SettingsPatch.PreferenceScreen.ALTERNATIVE_THUMBNAILS.addPreferences(
|
||||
NonInteractivePreference(
|
||||
"revanced_alt_thumbnail_about",
|
||||
null, // Summary is dynamically updated based on the current settings.
|
||||
tag = "app.revanced.integrations.youtube.settings.preference.AlternativeThumbnailsStatusPreference",
|
||||
ListPreference("revanced_alt_thumbnail_home",
|
||||
summaryKey = null,
|
||||
entriesKey = entries,
|
||||
entryValuesKey = values
|
||||
),
|
||||
ListPreference("revanced_alt_thumbnail_subscription",
|
||||
summaryKey = null,
|
||||
entriesKey = entries,
|
||||
entryValuesKey = values
|
||||
),
|
||||
ListPreference("revanced_alt_thumbnail_library",
|
||||
summaryKey = null,
|
||||
entriesKey = entries,
|
||||
entryValuesKey = values
|
||||
),
|
||||
ListPreference("revanced_alt_thumbnail_player",
|
||||
summaryKey = null,
|
||||
entriesKey = entries,
|
||||
entryValuesKey = values
|
||||
),
|
||||
ListPreference("revanced_alt_thumbnail_search",
|
||||
summaryKey = null,
|
||||
entriesKey = entries,
|
||||
entryValuesKey = values
|
||||
),
|
||||
SwitchPreference("revanced_alt_thumbnail_dearrow"),
|
||||
SwitchPreference("revanced_alt_thumbnail_dearrow_connection_toast"),
|
||||
TextPreference("revanced_alt_thumbnail_dearrow_api_url"),
|
||||
NonInteractivePreference(
|
||||
"revanced_alt_thumbnail_dearrow_about",
|
||||
// Custom about preference with link to the DeArrow website.
|
||||
tag = "app.revanced.integrations.youtube.settings.preference.AlternativeThumbnailsAboutDeArrowPreference",
|
||||
selectable = true,
|
||||
),
|
||||
SwitchPreference("revanced_alt_thumbnail_stills"),
|
||||
ListPreference("revanced_alt_thumbnail_stills_time", summaryKey = null),
|
||||
SwitchPreference("revanced_alt_thumbnail_stills_fast"),
|
||||
SwitchPreference("revanced_alt_thumbnail_dearrow_connection_toast"),
|
||||
TextPreference("revanced_alt_thumbnail_dearrow_api_url"),
|
||||
NonInteractivePreference("revanced_alt_thumbnail_stills_about"),
|
||||
SwitchPreference("revanced_alt_thumbnail_stills_fast"),
|
||||
ListPreference("revanced_alt_thumbnail_stills_time", summaryKey = null)
|
||||
)
|
||||
|
||||
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
|
||||
|
||||
@@ -2,16 +2,14 @@ package app.revanced.patches.youtube.misc.announcements
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
|
||||
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.misc.settings.SettingsPatch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.MainActivityFingerprint
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
|
||||
import app.revanced.util.resultOrThrow
|
||||
|
||||
@Patch(
|
||||
name = "Announcements",
|
||||
@@ -21,7 +19,7 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
)
|
||||
@Suppress("unused")
|
||||
object AnnouncementsPatch : BytecodePatch(
|
||||
setOf(MainActivityFingerprint)
|
||||
setOf(MainActivityOnCreateFingerprint)
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/announcements/AnnouncementsPatch;"
|
||||
@@ -33,16 +31,11 @@ object AnnouncementsPatch : BytecodePatch(
|
||||
SwitchPreference("revanced_announcements")
|
||||
)
|
||||
|
||||
val onCreateMethod = MainActivityFingerprint.result?.let {
|
||||
it.mutableClass.methods.find { method -> method.name == "onCreate" }
|
||||
} ?: throw MainActivityFingerprint.exception
|
||||
|
||||
val superCallIndex = onCreateMethod.getInstructions().indexOfFirst { it.opcode == Opcode.INVOKE_SUPER_RANGE }
|
||||
|
||||
onCreateMethod.addInstructions(
|
||||
superCallIndex + 1,
|
||||
"invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->showAnnouncement(Landroid/app/Activity;)V"
|
||||
MainActivityOnCreateFingerprint.resultOrThrow().mutableMethod.addInstructions(
|
||||
// Insert index must be great than the insert index used by GmsCoreSupport,
|
||||
// as both patch the same method and GmsCore check should be first.
|
||||
1,
|
||||
"invoke-static/range { p0 .. p0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->showAnnouncement(Landroid/app/Activity;)V"
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
|
||||
import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
|
||||
import app.revanced.patches.youtube.misc.gms.fingerprints.*
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.HomeActivityFingerprint
|
||||
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
|
||||
|
||||
@Suppress("unused")
|
||||
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
||||
@@ -23,7 +23,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
CastContextFetchFingerprint,
|
||||
),
|
||||
mainActivityOnCreateFingerprint = HomeActivityFingerprint,
|
||||
mainActivityOnCreateFingerprint = MainActivityOnCreateFingerprint,
|
||||
integrationsPatchDependency = IntegrationsPatch::class,
|
||||
dependencies = setOf(
|
||||
HideCastButtonPatch::class,
|
||||
@@ -57,5 +57,5 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
||||
PrimeMethodFingerprint,
|
||||
),
|
||||
) {
|
||||
override val gmsCoreVendor by gmsCoreVendorGroupIdOption
|
||||
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package app.revanced.patches.youtube.misc.navigation
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
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.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.fingerprints.*
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
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.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
@Patch(
|
||||
description = "Hooks the active navigation or search bar.",
|
||||
dependencies = [
|
||||
IntegrationsPatch::class,
|
||||
NavigationBarHookResourcePatch::class,
|
||||
],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object NavigationBarHookPatch : BytecodePatch(
|
||||
setOf(
|
||||
PivotBarConstructorFingerprint,
|
||||
NavigationEnumFingerprint,
|
||||
PivotBarButtonsCreateDrawableViewFingerprint,
|
||||
PivotBarButtonsCreateResourceViewFingerprint,
|
||||
NavigationBarHookCallbackFingerprint,
|
||||
ActionBarSearchResultsFingerprint,
|
||||
),
|
||||
) {
|
||||
internal const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/shared/NavigationBar;"
|
||||
|
||||
internal const val INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/shared/NavigationBar\$NavigationButton;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
fun MutableMethod.addHook(hook: Hook, insertPredicate: Instruction.() -> Boolean) {
|
||||
val filtered = getInstructions().filter(insertPredicate)
|
||||
if (filtered.isEmpty()) throw PatchException("Could not find insert indexes")
|
||||
filtered.forEach {
|
||||
val insertIndex = it.location.index + 2
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||
|
||||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$register }, " +
|
||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
InitializeButtonsFingerprint.apply {
|
||||
resolve(context, PivotBarConstructorFingerprint.resultOrThrow().classDef)
|
||||
}.resultOrThrow().mutableMethod.apply {
|
||||
// Hook the current navigation bar enum value. Note, the 'You' tab does not have an enum value.
|
||||
val navigationEnumClassName = NavigationEnumFingerprint.resultOrThrow().mutableClass.type
|
||||
addHook(Hook.SET_LAST_APP_NAVIGATION_ENUM) {
|
||||
opcode == Opcode.INVOKE_STATIC &&
|
||||
getReference<MethodReference>()?.definingClass == navigationEnumClassName
|
||||
}
|
||||
|
||||
// Hook the creation of navigation tab views.
|
||||
val drawableTabMethod = PivotBarButtonsCreateDrawableViewFingerprint.resultOrThrow().mutableMethod
|
||||
addHook(Hook.NAVIGATION_TAB_LOADED) predicate@{
|
||||
MethodUtil.methodSignaturesMatch(
|
||||
getReference<MethodReference>() ?: return@predicate false,
|
||||
drawableTabMethod,
|
||||
)
|
||||
}
|
||||
|
||||
val imageResourceTabMethod = PivotBarButtonsCreateResourceViewFingerprint.resultOrThrow().method
|
||||
addHook(Hook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{
|
||||
MethodUtil.methodSignaturesMatch(
|
||||
getReference<MethodReference>() ?: return@predicate false,
|
||||
imageResourceTabMethod,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Hook the search bar.
|
||||
|
||||
// Two different layouts are used at the hooked code.
|
||||
// Insert before the first ViewGroup method call after inflating,
|
||||
// so this works regardless which layout is used.
|
||||
ActionBarSearchResultsFingerprint.resultOrThrow().mutableMethod.apply {
|
||||
val instructionIndex = indexOfFirstInstruction {
|
||||
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setLayoutDirection"
|
||||
}
|
||||
|
||||
val viewRegister = getInstruction<FiveRegisterInstruction>(instructionIndex).registerC
|
||||
|
||||
addInstruction(
|
||||
instructionIndex,
|
||||
"invoke-static { v$viewRegister }, " +
|
||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val hookNavigationButtonCreated: (String) -> Unit by lazy {
|
||||
val method = NavigationBarHookCallbackFingerprint.resultOrThrow().mutableMethod
|
||||
|
||||
{ integrationsClassDescriptor ->
|
||||
method.addInstruction(
|
||||
0,
|
||||
"invoke-static { p0, p1 }, " +
|
||||
"$integrationsClassDescriptor->navigationTabCreated" +
|
||||
"(${INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Hook(val methodName: String, val parameters: String) {
|
||||
SET_LAST_APP_NAVIGATION_ENUM("setLastAppNavigationEnum", "Ljava/lang/Enum;"),
|
||||
NAVIGATION_TAB_LOADED("navigationTabLoaded", "Landroid/view/View;"),
|
||||
NAVIGATION_IMAGE_RESOURCE_TAB_LOADED("navigationImageResourceTabLoaded", "Landroid/view/View;"),
|
||||
SEARCH_BAR_RESULTS_VIEW_LOADED("searchBarResultsViewLoaded", "Landroid/view/View;"),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package app.revanced.patches.youtube.misc.navigation
|
||||
|
||||
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 NavigationBarHookResourcePatch : ResourcePatch() {
|
||||
internal var imageOnlyTabResourceId: Long = -1
|
||||
internal var actionBarSearchResultsViewMicId: Long = -1
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
imageOnlyTabResourceId = ResourceMappingPatch.resourceMappings.first {
|
||||
it.type == "layout" && it.name == "image_only_tab"
|
||||
}.id
|
||||
|
||||
actionBarSearchResultsViewMicId = ResourceMappingPatch.resourceMappings.first {
|
||||
it.type == "layout" && it.name == "action_bar_search_results_view_mic"
|
||||
}.id
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object ActionBarSearchResultsFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Landroid/view/View;",
|
||||
parameters = listOf("Landroid/view/LayoutInflater;"),
|
||||
literalSupplier = { NavigationBarHookResourcePatch.actionBarSearchResultsViewMicId }
|
||||
)
|
||||
@@ -1,13 +1,16 @@
|
||||
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
|
||||
package app.revanced.patches.youtube.misc.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.ResolvePivotBarFingerprintsPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
/**
|
||||
* Resolves to the class found in [PivotBarConstructorFingerprint].
|
||||
*/
|
||||
internal object InitializeButtonsFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf(),
|
||||
literalSupplier = { ResolvePivotBarFingerprintsPatch.imageOnlyTabResourceId }
|
||||
literalSupplier = { NavigationBarHookResourcePatch.imageOnlyTabResourceId }
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
package app.revanced.patches.youtube.misc.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.buttons.navigation.NavigationButtonsPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
/**
|
||||
* Integrations method, used for callback into to other patches.
|
||||
* Specifically, [NavigationButtonsPatch].
|
||||
*/
|
||||
internal object NavigationBarHookCallbackFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
returnType = "V",
|
||||
parameters = listOf(NavigationBarHookPatch.INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR, "Landroid/view/View;"),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.name == "navigationTabCreatedCallback" &&
|
||||
methodDef.definingClass == NavigationBarHookPatch.INTEGRATIONS_CLASS_DESCRIPTOR
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
package app.revanced.patches.youtube.misc.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
/**
|
||||
* Resolves to the Enum class that looks up ordinal -> instance.
|
||||
*/
|
||||
internal object NavigationEnumFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR,
|
||||
strings = listOf(
|
||||
"PIVOT_HOME",
|
||||
"TAB_SHORTS",
|
||||
"CREATION_TAB_LARGE",
|
||||
"PIVOT_SUBSCRIPTIONS",
|
||||
"TAB_ACTIVITY",
|
||||
"VIDEO_LIBRARY_WHITE",
|
||||
"INCOGNITO_CIRCLE"
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.youtube.misc.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object PivotBarButtonsCreateDrawableViewFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
// Method has different number of parameters in some app targets.
|
||||
// Parameters are checked in custom fingerprint.
|
||||
returnType = "Landroid/view/View;",
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" &&
|
||||
// Only one method has a Drawable parameter.
|
||||
methodDef.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.youtube.misc.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object PivotBarButtonsCreateResourceViewFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("L", "Z", "I", "L"),
|
||||
returnType = "Landroid/view/View;",
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;"
|
||||
}
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints
|
||||
package app.revanced.patches.youtube.misc.navigation.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.youtube.shared.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object MainActivityOnCreateFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
parameters = listOf("Landroid/os/Bundle;"),
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
methodDef.name == "onCreate" &&
|
||||
(classDef.type.endsWith("MainActivity;")
|
||||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|
||||
|| classDef.type.endsWith("WatchWhileActivity;"))
|
||||
}
|
||||
)
|
||||
@@ -44,15 +44,28 @@
|
||||
</string-array>
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
|
||||
<string-array name="revanced_alt_thumbnail_options_entries">
|
||||
<item>@string/revanced_alt_thumbnail_options_entry_1</item>
|
||||
<item>@string/revanced_alt_thumbnail_options_entry_2</item>
|
||||
<item>@string/revanced_alt_thumbnail_options_entry_3</item>
|
||||
<item>@string/revanced_alt_thumbnail_options_entry_4</item>
|
||||
</string-array>
|
||||
<string-array name="revanced_alt_thumbnail_options_entry_values">
|
||||
<!-- Enum names from Integrations -->
|
||||
<item>ORIGINAL</item>
|
||||
<item>DEARROW</item>
|
||||
<item>DEARROW_STILL_IMAGES</item>
|
||||
<item>STILL_IMAGES</item>
|
||||
</string-array>
|
||||
<string-array name="revanced_alt_thumbnail_stills_time_entries">
|
||||
<item>@string/revanced_alt_thumbnail_stills_time_entry_1</item>
|
||||
<item>@string/revanced_alt_thumbnail_stills_time_entry_2</item>
|
||||
<item>@string/revanced_alt_thumbnail_stills_time_entry_3</item>
|
||||
</string-array>
|
||||
<string-array name="revanced_alt_thumbnail_stills_time_entry_values">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>BEGINNING</item>
|
||||
<item>MIDDLE</item>
|
||||
<item>END</item>
|
||||
</string-array>
|
||||
</patch>
|
||||
<patch id="video.quality.RememberVideoQualityPatch">
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
<string name="revanced_settings_import_failure_parse">Import failed: %s</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<string name="gms_core_not_installed_warning">GmsCore is not installed. Please install.</string>
|
||||
<string name="gms_core_not_running_warning">GmsCore is failing to run. Please follow the \"Don\'t kill my app\" guide for GmsCore.</string>
|
||||
<string name="gms_core_toast_not_installed_message">GmsCore is not installed. Install it.</string>
|
||||
<string name="gms_core_toast_not_whitelisted_message">Follow the \"Don\'t kill my app\" guide for GmsCore.</string>
|
||||
<string name="gms_core_dialog_title">Action needed</string>
|
||||
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">GmsCore is not whitelisted from battery optimization.\n\nFollow the \"Don\'t kill my app\" guide for GmsCore.</string>
|
||||
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">GmsCore does not have permission to run in the background.\n\nFollow the \"Don\'t kill my app\" guide for GmsCore.</string>
|
||||
<string name="gms_core_dialog_ok_button_text">Open website</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
@@ -167,9 +171,24 @@
|
||||
<string name="revanced_custom_filter_summary_off">Custom filter is disabled</string>
|
||||
<string name="revanced_custom_filter_strings_title">Custom filter</string>
|
||||
<string name="revanced_custom_filter_strings_summary">List of component path builder strings to filter separated by new line</string>
|
||||
<string name="revanced_custom_filter_toast_invalid_characters">Invalid custom filter (must be ASCII only): %s</string>
|
||||
<string name="revanced_custom_filter_toast_invalid_syntax">Invalid custom filter: %s</string>
|
||||
<string name="revanced_custom_filter_toast_reset">Custom filter reset to default</string>
|
||||
<string name="revanced_hide_keyword_content_screen_title">Hide keyword content</string>
|
||||
<string name="revanced_hide_keyword_content_screen_summary">Hide search and feed videos using keyword filters</string>
|
||||
<string name="revanced_hide_keyword_content_home_title">Hide home videos by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_home_summary_on">Videos in the home tab are filtered by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_home_summary_off">Videos in the home tab are not filtered by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_title">Hide subscription videos by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_summary_on">Videos in the subscriptions tab are filtered by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_subscriptions_summary_off">Videos in the subscriptions tab are not filtered by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_search_title">Hide search results by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_search_summary_on">Search results are filtered by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_search_summary_off">Search results are not filtered by keywords</string>
|
||||
<string name="revanced_hide_keyword_content_phrases_title">Keywords to hide</string>
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">Keywords and phrases to hide, separated by new lines\n\nWords with uppercase letters in the middle must be entered with the casing (ie: iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">About keyword filtering</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Home/Subscription/Search results are filtered to hide content that matches keyword phrases\n\nLimitations\n• Some Shorts may not be hidden\n• Some UI components may not be hidden\n• Searching for a keyword may show no results</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common" formatted="false">Invalid keyword. Cannot use: \'%s\' as a filter</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_length" formatted="false">Invalid keyword. \'%s\' is less than %s characters</string>
|
||||
</patch>
|
||||
<patch id="ad.general.HideAdsResourcePatch">
|
||||
<string name="revanced_hide_general_ads_title">Hide general ads</string>
|
||||
@@ -464,9 +483,15 @@
|
||||
<string name="revanced_hide_seekbar_thumbnail_summary_off">Thumbnail seekbar is shown</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
|
||||
<string name="revanced_hide_shorts_title">Hide Shorts in feed</string>
|
||||
<string name="revanced_hide_shorts_summary_on">Shorts are hidden</string>
|
||||
<string name="revanced_hide_shorts_summary_off">Shorts are shown</string>
|
||||
<string name="revanced_hide_shorts_home_title">Hide Shorts in home feed</string>
|
||||
<string name="revanced_hide_shorts_home_summary_on">Shorts in home feed are hidden</string>
|
||||
<string name="revanced_hide_shorts_home_summary_off">Shorts in home feed are shown</string>
|
||||
<string name="revanced_hide_shorts_subscriptions_title">Hide Shorts in subscription feed</string>
|
||||
<string name="revanced_hide_shorts_subscriptions_summary_on">Shorts in subscription feed are hidden</string>
|
||||
<string name="revanced_hide_shorts_subscriptions_summary_off">Shorts in subscription feed are shown</string>
|
||||
<string name="revanced_hide_shorts_search_title">Hide Shorts in search results</string>
|
||||
<string name="revanced_hide_shorts_search_summary_on">Shorts in search results are hidden</string>
|
||||
<string name="revanced_hide_shorts_search_summary_off">Shorts in search results are shown</string>
|
||||
<string name="revanced_hide_shorts_join_button_title">Hide join button</string>
|
||||
<string name="revanced_hide_shorts_join_button_summary_on">Join button is hidden</string>
|
||||
<string name="revanced_hide_shorts_join_button_summary_off">Join button is shown</string>
|
||||
@@ -826,33 +851,31 @@
|
||||
<string name="revanced_seekbar_custom_color_invalid">Invalid seekbar color value. Using default value.</string>
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
|
||||
<string name="revanced_alt_thumbnail_about_title">Thumbnails in use</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_title">Enable DeArrow thumbnails</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_summary_on">Using DeArrow thumbnails</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_summary_off">Not using DeArrow thumbnails</string>
|
||||
<string name="revanced_alt_thumbnail_home_title">Home tab</string>
|
||||
<string name="revanced_alt_thumbnail_subscription_title">Subscription tab</string>
|
||||
<string name="revanced_alt_thumbnail_library_title">You tab</string>
|
||||
<string name="revanced_alt_thumbnail_player_title">Player playlists, recommendations</string>
|
||||
<string name="revanced_alt_thumbnail_search_title">Search results</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_1">Original thumbnails</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & Original thumbnails</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & Still captures</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_4">Still captures</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_title">DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">DeArrow provides crowd-sourced thumbnails for YouTube videos. These thumbnails are often more relevant than those provided by YouTube\n\nIf enabled, video URLs will be sent to the API server and no other data is sent. If a video does not have DeArrow thumbnails, then the original or still captures are shown\n\nTap here to learn more about DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">Show a toast if API is not available</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">Toast is shown if DeArrow is not available</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">Toast is not shown if DeArrow is not available</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_api_url_title">DeArrow API endpoint</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">The URL of the DeArrow thumbnail cache endpoint. Do not change this unless you know what you\'re doing</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_title">About DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">DeArrow provides crowd-sourced thumbnails for YouTube videos. These thumbnails are often more relevant than those provided by YouTube. If enabled, video URLs will be sent to the API server and no other data is sent\n\nTap here to learn more about DeArrow</string>
|
||||
<string name="revanced_alt_thumbnail_stills_title">Enable still video captures</string>
|
||||
<string name="revanced_alt_thumbnail_stills_summary_on">Using YouTube still video captures</string>
|
||||
<string name="revanced_alt_thumbnail_stills_summary_off">Not using YouTube still video captures</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_title">Video time to take the still from</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_entry_1">Beginning of video</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_entry_2">Middle of video</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_entry_3">End of video</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">The URL of the DeArrow thumbnail cache endpoint</string>
|
||||
<string name="revanced_alt_thumbnail_stills_about_title">Still video captures</string>
|
||||
<string name="revanced_alt_thumbnail_stills_about_summary">Still captures are taken from the beginning/middle/end of each video. These images are built into YouTube and no external API is used</string>
|
||||
<string name="revanced_alt_thumbnail_stills_fast_title">Use fast still captures</string>
|
||||
<string name="revanced_alt_thumbnail_stills_fast_summary_on">Using medium quality still captures. Thumbnails will load faster, but live streams, unreleased, or very old videos may show blank thumbnails</string>
|
||||
<string name="revanced_alt_thumbnail_stills_fast_summary_off">Using high quality still captures</string>
|
||||
<string name="revanced_alt_thumbnail_stills_about_title">About still video captures</string>
|
||||
<string name="revanced_alt_thumbnail_stills_about_summary">Still captures are taken from the beginning/middle/end of each video. These images are built into YouTube and no external API is used</string>
|
||||
<string name="revanced_alt_thumbnail_about_status_disabled">Showing original YouTube thumbnails</string>
|
||||
<string name="revanced_alt_thumbnail_about_status_stills">Showing still video captures</string>
|
||||
<string name="revanced_alt_thumbnail_about_status_dearrow">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then the original YouTube thumbnails are shown</string>
|
||||
<string name="revanced_alt_thumbnail_about_status_dearrow_stills">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then still video captures are shown</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_title">Video time to take still captures from</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_entry_1">Beginning of video</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_entry_2">Middle of video</string>
|
||||
<string name="revanced_alt_thumbnail_stills_time_entry_3">End of video</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_error">DeArrow temporarily not available (status code: %s)</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_error_generic">DeArrow temporarily not available</string>
|
||||
</patch>
|
||||
@@ -862,6 +885,7 @@
|
||||
<string name="revanced_announcements_summary_off">Announcements are not shown on startup</string>
|
||||
<string name="revanced_announcements_enabled_summary">Show announcements on startup</string>
|
||||
<string name="revanced_announcements_connection_failed">Failed connecting to announcements provider</string>
|
||||
<string name="revanced_announcements_dialog_dismiss">Dismiss</string>
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
<string name="revanced_auto_repeat_title">Enable auto-repeat</string>
|
||||
|
||||
Reference in New Issue
Block a user