mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-20 09:33:57 +00:00
Compare commits
13 Commits
v4.9.0-dev
...
v4.10.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c979e92676 | ||
|
|
b78b7cfe6c | ||
|
|
4919cba478 | ||
|
|
1d46b95698 | ||
|
|
70013d813b | ||
|
|
8a33fe9986 | ||
|
|
ee21a2bb91 | ||
|
|
ea67466bd5 | ||
|
|
320e039a9f | ||
|
|
6b9c13d92c | ||
|
|
8e67cdca55 | ||
|
|
32c5fa4d04 | ||
|
|
61401c9ea4 |
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,3 +1,49 @@
|
|||||||
|
# [4.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.1...v4.10.0-dev.2) (2024-06-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube Music:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3315](https://github.com/ReVanced/revanced-patches/issues/3315)) ([3c31e55](https://github.com/ReVanced/revanced-patches/commit/3c31e55b13d9495e857f068f8cd2b4320112d763))
|
||||||
|
* **YouTube:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3314](https://github.com/ReVanced/revanced-patches/issues/3314)) ([37d415b](https://github.com/ReVanced/revanced-patches/commit/37d415b53af4771d9c97a8b1c153be32bf3ac2e0))
|
||||||
|
|
||||||
|
# [4.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.9.0...v4.10.0-dev.1) (2024-06-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Miniplayer:** Rename `Tablet mini player` and allow selecting the style of the in-app miniplayer ([#3302](https://github.com/ReVanced/revanced-patches/issues/3302)) ([5511736](https://github.com/ReVanced/revanced-patches/commit/5511736b0c5dd409db6a68db0f85e389bb95be47))
|
||||||
|
|
||||||
|
# [4.9.0](https://github.com/ReVanced/revanced-patches/compare/v4.8.3...v4.9.0) (2024-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof client:** Allow swipe gestures to enter/exit fullscreen when spoofing with `Android VR` client ([#3259](https://github.com/ReVanced/revanced-patches/issues/3259)) ([5114900](https://github.com/ReVanced/revanced-patches/commit/5114900b1b5572c04ba6759eedab77f0a934b058))
|
||||||
|
* **YouTube - Spoof client:** Restore playback speed menu when spoofing to an iOS client ([95f290f](https://github.com/ReVanced/revanced-patches/commit/95f290f1139cc8679beecac53c623847668f885e))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Messenger:** Add `Hide inbox subtabs` patch ([#3163](https://github.com/ReVanced/revanced-patches/issues/3163)) ([24e4ebd](https://github.com/ReVanced/revanced-patches/commit/24e4ebd77ad0f349b479926bf3983b72c2683496))
|
||||||
|
* **YouTube - Hide layout components:** Disable like / subscribe button glow animation ([#3265](https://github.com/ReVanced/revanced-patches/issues/3265)) ([68d35ea](https://github.com/ReVanced/revanced-patches/commit/68d35eafc15513c23cd5220260023e7ec5b7978a))
|
||||||
|
* **YouTube - Playback speed:** Add option to show speed dialog button in video player ([#3197](https://github.com/ReVanced/revanced-patches/issues/3197)) ([ad00305](https://github.com/ReVanced/revanced-patches/commit/ad00305ff57d5e8041de7375bea7d3ad6f18c4e2))
|
||||||
|
* **YouTube Music:** Support version `7.03` ([#3272](https://github.com/ReVanced/revanced-patches/issues/3272)) ([d1ceca3](https://github.com/ReVanced/revanced-patches/commit/d1ceca39984f7933b28d81802d04bb3ead327595))
|
||||||
|
* **YouTube:** Support version `19.12`, `19.13`, `19.14`, `19.15` and `19.16` ([#3239](https://github.com/ReVanced/revanced-patches/issues/3239)) ([99b07e0](https://github.com/ReVanced/revanced-patches/commit/99b07e0e18574668f36bb3c962c8d11222114be4))
|
||||||
|
|
||||||
|
# [4.9.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.6...v4.9.0-dev.7) (2024-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Playback speed:** Add option to show speed dialog button in video player ([#3197](https://github.com/ReVanced/revanced-patches/issues/3197)) ([ad00305](https://github.com/ReVanced/revanced-patches/commit/ad00305ff57d5e8041de7375bea7d3ad6f18c4e2))
|
||||||
|
|
||||||
|
# [4.9.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.5...v4.9.0-dev.6) (2024-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof client:** Restore playback speed menu when spoofing to an iOS client ([95f290f](https://github.com/ReVanced/revanced-patches/commit/95f290f1139cc8679beecac53c623847668f885e))
|
||||||
|
|
||||||
# [4.9.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.4...v4.9.0-dev.5) (2024-06-01)
|
# [4.9.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.4...v4.9.0-dev.5) (2024-06-01)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ with the maintainers of ReVanced Patches. This will help you determine whether y
|
|||||||
and whether it is worth your time to implement it
|
and whether it is worth your time to implement it
|
||||||
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
||||||
3. Commit your changes. In case you are contributing a new patch, make sure to follow the conventions for patches
|
3. Commit your changes. In case you are contributing a new patch, make sure to follow the conventions for patches
|
||||||
described in the [documentation](https://github.com/ReVanced/revanced-patches/tree/docs/docs)
|
described in the [ReVanced Patcher documentation](https://github.com/ReVanced/revanced-patcher/tree/main/docs)
|
||||||
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
||||||
that your pull request closes in the description of your pull request
|
that your pull request closes in the description of your pull request
|
||||||
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
||||||
|
|||||||
@@ -403,6 +403,12 @@ public final class app/revanced/patches/music/misc/androidauto/BypassCertificate
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch;
|
||||||
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/music/misc/gms/Constants {
|
public final class app/revanced/patches/music/misc/gms/Constants {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/Constants;
|
public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/Constants;
|
||||||
}
|
}
|
||||||
@@ -1528,6 +1534,12 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch;
|
||||||
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch : app/revanced/patcher/patch/BytecodePatch {
|
public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch;
|
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch;
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
@@ -1632,6 +1644,12 @@ public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch;
|
||||||
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/debugging/DebuggingPatch : app/revanced/patcher/patch/ResourcePatch {
|
public final class app/revanced/patches/youtube/misc/debugging/DebuggingPatch : app/revanced/patcher/patch/ResourcePatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch;
|
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch;
|
||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
@@ -1840,6 +1858,12 @@ public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch :
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch;
|
||||||
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
|
public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch;
|
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch;
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
@@ -1888,6 +1912,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
|
|||||||
public final class app/revanced/util/BytecodeUtilsKt {
|
public final class app/revanced/util/BytecodeUtilsKt {
|
||||||
public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
||||||
public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
||||||
|
public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||||
public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException;
|
public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException;
|
||||||
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
|
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
|
||||||
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
|
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
|
||||||
@@ -1895,6 +1920,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
|
public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
|
||||||
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
||||||
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
|
public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 4.9.0-dev.5
|
version = 4.10.0-dev.2
|
||||||
|
|||||||
@@ -1,50 +1,13 @@
|
|||||||
package app.revanced.patches.music.layout.minimizedplayback
|
package app.revanced.patches.music.layout.minimizedplayback
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
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.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patches.music.misc.backgroundplayback.BackgroundPlaybackPatch
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.BackgroundPlaybackDisableFingerprint
|
|
||||||
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint
|
|
||||||
import app.revanced.util.exception
|
|
||||||
|
|
||||||
@Patch(
|
@Deprecated("This patch has been merged into BackgroundPlaybackPatch.")
|
||||||
name = "Minimized playback",
|
|
||||||
description = "Unlocks options for picture-in-picture and background playback.",
|
|
||||||
compatiblePackages = [
|
|
||||||
CompatiblePackage(
|
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@Suppress("unused")
|
|
||||||
object MinimizedPlaybackPatch : BytecodePatch(
|
object MinimizedPlaybackPatch : BytecodePatch(
|
||||||
setOf(
|
dependencies = setOf(BackgroundPlaybackPatch::class),
|
||||||
KidsMinimizedPlaybackPolicyControllerFingerprint,
|
|
||||||
BackgroundPlaybackDisableFingerprint,
|
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
KidsMinimizedPlaybackPolicyControllerFingerprint.result?.mutableMethod?.addInstruction(
|
|
||||||
0,
|
|
||||||
"return-void",
|
|
||||||
) ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception
|
|
||||||
|
|
||||||
BackgroundPlaybackDisableFingerprint.result?.mutableMethod?.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const/4 v0, 0x1
|
|
||||||
return v0
|
|
||||||
""",
|
|
||||||
) ?: throw BackgroundPlaybackDisableFingerprint.exception
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package app.revanced.patches.music.misc.backgroundplayback
|
||||||
|
|
||||||
|
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.misc.backgroundplayback.fingerprints.BackgroundPlaybackDisableFingerprint
|
||||||
|
import app.revanced.patches.music.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
name = "Remove background playback restrictions",
|
||||||
|
description = "Removes restrictions on background playback.",
|
||||||
|
compatiblePackages = [
|
||||||
|
CompatiblePackage(
|
||||||
|
"com.google.android.apps.youtube.music",
|
||||||
|
[
|
||||||
|
"6.45.54",
|
||||||
|
"6.51.53",
|
||||||
|
"7.01.53",
|
||||||
|
"7.02.52",
|
||||||
|
"7.03.52",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object BackgroundPlaybackPatch : BytecodePatch(
|
||||||
|
setOf(
|
||||||
|
KidsBackgroundPlaybackPolicyControllerFingerprint,
|
||||||
|
BackgroundPlaybackDisableFingerprint,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
KidsBackgroundPlaybackPolicyControllerFingerprint.resultOrThrow().mutableMethod.addInstruction(
|
||||||
|
0,
|
||||||
|
"return-void",
|
||||||
|
)
|
||||||
|
|
||||||
|
BackgroundPlaybackDisableFingerprint.resultOrThrow().mutableMethod.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const/4 v0, 0x1
|
||||||
|
return v0
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
|
package app.revanced.patches.music.misc.backgroundplayback.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
|
package app.revanced.patches.music.misc.backgroundplayback.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint(
|
internal object KidsBackgroundPlaybackPolicyControllerFingerprint : MethodFingerprint(
|
||||||
"V",
|
"V",
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
listOf("I", "L", "Z"),
|
listOf("I", "L", "Z"),
|
||||||
@@ -2,10 +2,11 @@ package app.revanced.patches.music.premium.backgroundplay
|
|||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patches.music.layout.minimizedplayback.MinimizedPlaybackPatch
|
import app.revanced.patches.music.misc.backgroundplayback.BackgroundPlaybackPatch
|
||||||
@Deprecated("This patch has been merged into MinimizedPlaybackPatch.")
|
|
||||||
|
@Deprecated("This patch has been merged into BackgroundPlaybackPatch.")
|
||||||
object BackgroundPlayPatch : BytecodePatch(
|
object BackgroundPlayPatch : BytecodePatch(
|
||||||
dependencies = setOf(MinimizedPlaybackPatch::class),
|
dependencies = setOf(BackgroundPlaybackPatch::class),
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package app.revanced.patches.shared.misc.mapping
|
package app.revanced.patches.shared.misc.mapping
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -51,9 +52,10 @@ object ResourceMappingPatch : ResourcePatch() {
|
|||||||
threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
|
threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(type: String, name: String) = resourceMappings.first {
|
operator fun get(type: String, name: String) =
|
||||||
it.type == type && it.name == name
|
resourceMappings.firstOrNull {
|
||||||
}.id
|
it.type == type && it.name == name
|
||||||
|
}?.id ?: throw PatchException("Could not find resource type: $type name: $name")
|
||||||
|
|
||||||
data class ResourceElement(val type: String, val name: String, val id: Long)
|
data class ResourceElement(val type: String, val name: String, val id: Long)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,370 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
|
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.shared.misc.settings.preference.TextPreference
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerClose
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerExpand
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerForwardButton
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerRewindButton
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.scrimOverlay
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlineXWhite24
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerDimensionsCalculatorParentFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernAddViewListenerFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernCloseButtonFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernConstructorFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandButtonFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandCloseDrawablesFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernForwardButtonFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernOverlayViewFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernRewindButtonFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernViewParentFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideNoContextFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerResponseModelSizeCheckFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
|
||||||
|
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
|
import app.revanced.util.findOpcodeIndicesReversed
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
|
|
||||||
|
// YT uses "Miniplayer" without a space between 'mini' and 'player: https://support.google.com/youtube/answer/9162927.
|
||||||
|
@Patch(
|
||||||
|
name = "Miniplayer",
|
||||||
|
description = "Adds options to change the in app minimized player, " +
|
||||||
|
"and if patching target 19.16+ adds options to use modern miniplayers.",
|
||||||
|
dependencies = [
|
||||||
|
IntegrationsPatch::class,
|
||||||
|
SettingsPatch::class,
|
||||||
|
AddResourcesPatch::class,
|
||||||
|
MiniplayerResourcePatch::class
|
||||||
|
],
|
||||||
|
compatiblePackages = [
|
||||||
|
CompatiblePackage(
|
||||||
|
"com.google.android.youtube", [
|
||||||
|
"18.32.39",
|
||||||
|
"18.37.36",
|
||||||
|
"18.38.44",
|
||||||
|
"18.43.45",
|
||||||
|
"18.44.41",
|
||||||
|
"18.45.43",
|
||||||
|
"18.48.39",
|
||||||
|
"18.49.37",
|
||||||
|
"19.01.34",
|
||||||
|
"19.02.39",
|
||||||
|
"19.03.36",
|
||||||
|
"19.04.38",
|
||||||
|
"19.05.36",
|
||||||
|
"19.06.39",
|
||||||
|
"19.07.40",
|
||||||
|
"19.08.36",
|
||||||
|
"19.09.38",
|
||||||
|
"19.10.39",
|
||||||
|
"19.11.43",
|
||||||
|
"19.12.41",
|
||||||
|
"19.13.37",
|
||||||
|
// 19.14 is left out, as it has incomplete miniplayer code and missing some UI resources.
|
||||||
|
// It's simpler to not bother with supporting this single old version.
|
||||||
|
// 19.15 has a different code for handling sub title texts,
|
||||||
|
// and also probably not worth making changes just to support this single old version.
|
||||||
|
"19.16.39" // Earliest supported version with modern miniplayers.
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object MiniplayerPatch : BytecodePatch(
|
||||||
|
setOf(GetFormFactorFingerprint,
|
||||||
|
MiniplayerDimensionsCalculatorParentFingerprint,
|
||||||
|
MiniplayerResponseModelSizeCheckFingerprint,
|
||||||
|
MiniplayerOverrideFingerprint,
|
||||||
|
MiniplayerModernConstructorFingerprint,
|
||||||
|
MiniplayerModernViewParentFingerprint,
|
||||||
|
YouTubePlayerOverlaysLayoutFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/MiniplayerPatch;"
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
|
// Modern mini player is only present and functional in 19.15+.
|
||||||
|
// Resource is not present in older versions. Using it to determine, if patching an old version.
|
||||||
|
val isPatchingOldVersion = ytOutlinePictureInPictureWhite24 < 0
|
||||||
|
|
||||||
|
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
|
||||||
|
PreferenceScreen(
|
||||||
|
key = "revanced_miniplayer_screen",
|
||||||
|
sorting = Sorting.UNSORTED,
|
||||||
|
preferences =
|
||||||
|
if (isPatchingOldVersion) {
|
||||||
|
setOf(
|
||||||
|
ListPreference(
|
||||||
|
"revanced_miniplayer_type",
|
||||||
|
summaryKey = null,
|
||||||
|
entriesKey = "revanced_miniplayer_type_legacy_entries",
|
||||||
|
entryValuesKey = "revanced_miniplayer_type_legacy_entry_values"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
setOf(
|
||||||
|
ListPreference(
|
||||||
|
"revanced_miniplayer_type",
|
||||||
|
summaryKey = null,
|
||||||
|
entriesKey = "revanced_miniplayer_type_19_15_entries",
|
||||||
|
entryValuesKey = "revanced_miniplayer_type_19_15_entry_values"
|
||||||
|
),
|
||||||
|
SwitchPreference("revanced_miniplayer_hide_expand_close"),
|
||||||
|
SwitchPreference("revanced_miniplayer_hide_subtext"),
|
||||||
|
SwitchPreference("revanced_miniplayer_hide_rewind_forward"),
|
||||||
|
TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// region Enable tablet miniplayer.
|
||||||
|
|
||||||
|
MiniplayerOverrideNoContextFingerprint.resolve(
|
||||||
|
context,
|
||||||
|
MiniplayerDimensionsCalculatorParentFingerprint.resultOrThrow().classDef
|
||||||
|
)
|
||||||
|
MiniplayerOverrideNoContextFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Legacy tablet Miniplayer hooks.
|
||||||
|
|
||||||
|
MiniplayerOverrideFingerprint.resultOrThrow().let {
|
||||||
|
val appNameStringIndex = it.scanResult.stringsScanResult!!.matches.first().index + 2
|
||||||
|
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
val walkerMethod = context.toMethodWalker(this)
|
||||||
|
.nextMethod(appNameStringIndex, true)
|
||||||
|
.getMethod() as MutableMethod
|
||||||
|
|
||||||
|
walkerMethod.apply {
|
||||||
|
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MiniplayerResponseModelSizeCheckFingerprint.resultOrThrow().let {
|
||||||
|
it.mutableMethod.insertLegacyTabletMiniplayerOverride(it.scanResult.patternScanResult!!.endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPatchingOldVersion) {
|
||||||
|
// Return here, as patch below is only intended for new versions of the app.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
|
||||||
|
// region Enable modern miniplayer.
|
||||||
|
|
||||||
|
MiniplayerModernConstructorFingerprint.resultOrThrow().mutableClass.methods.forEach {
|
||||||
|
it.apply {
|
||||||
|
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||||
|
val iPutIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
this.opcode == Opcode.IPUT && this.getReference<FieldReference>()?.type == "I"
|
||||||
|
}
|
||||||
|
|
||||||
|
insertModernMiniplayerTypeOverride(iPutIndex)
|
||||||
|
} else {
|
||||||
|
findReturnIndicesReversed().forEach { index -> insertModernMiniplayerOverride(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Fix 19.16 using mixed up drawables for tablet modern.
|
||||||
|
// YT fixed this mistake in 19.17.
|
||||||
|
// Fix this, by swapping the drawable resource values with each other.
|
||||||
|
|
||||||
|
MiniplayerModernExpandCloseDrawablesFingerprint.apply {
|
||||||
|
resolve(
|
||||||
|
context,
|
||||||
|
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
|
||||||
|
)
|
||||||
|
}.resultOrThrow().mutableMethod.apply {
|
||||||
|
listOf(
|
||||||
|
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
|
||||||
|
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
|
||||||
|
).forEach { (originalResource, replacementResource) ->
|
||||||
|
val imageResourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(originalResource)
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
|
||||||
|
|
||||||
|
replaceInstruction(imageResourceIndex, "const v$register, $replacementResource")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
|
||||||
|
// region Add hooks to hide tablet modern miniplayer buttons.
|
||||||
|
|
||||||
|
listOf(
|
||||||
|
Triple(MiniplayerModernExpandButtonFingerprint, modernMiniplayerExpand,"hideMiniplayerExpandClose"),
|
||||||
|
Triple(MiniplayerModernCloseButtonFingerprint, modernMiniplayerClose, "hideMiniplayerExpandClose"),
|
||||||
|
Triple(MiniplayerModernRewindButtonFingerprint, modernMiniplayerRewindButton, "hideMiniplayerRewindForward"),
|
||||||
|
Triple(MiniplayerModernForwardButtonFingerprint, modernMiniplayerForwardButton, "hideMiniplayerRewindForward"),
|
||||||
|
Triple(MiniplayerModernOverlayViewFingerprint, scrimOverlay, "adjustMiniplayerOpacity")
|
||||||
|
).forEach { (fingerprint, literalValue, methodName) ->
|
||||||
|
fingerprint.resolve(
|
||||||
|
context,
|
||||||
|
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
|
||||||
|
)
|
||||||
|
|
||||||
|
fingerprint.hookInflatedView(
|
||||||
|
literalValue,
|
||||||
|
"Landroid/widget/ImageView;",
|
||||||
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
MiniplayerModernAddViewListenerFingerprint.apply {
|
||||||
|
resolve(
|
||||||
|
context,
|
||||||
|
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
|
||||||
|
)
|
||||||
|
}.resultOrThrow().mutableMethod.addInstruction(
|
||||||
|
0,
|
||||||
|
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" +
|
||||||
|
"hideMiniplayerSubTexts(Landroid/view/View;)V"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Modern 2 has a broken overlay subtitle view that is always present.
|
||||||
|
// Modern 2 uses the same overlay controls as the regular video player,
|
||||||
|
// and the overlay views are added at runtime.
|
||||||
|
// Add a hook to the overlay class, and pass the added views to integrations.
|
||||||
|
YouTubePlayerOverlaysLayoutFingerprint.resultOrThrow().mutableClass.methods.add(
|
||||||
|
ImmutableMethod(
|
||||||
|
YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME,
|
||||||
|
"addView",
|
||||||
|
listOf(
|
||||||
|
ImmutableMethodParameter("Landroid/view/View;", null, null),
|
||||||
|
ImmutableMethodParameter("I", null, null),
|
||||||
|
ImmutableMethodParameter("Landroid/view/ViewGroup\$LayoutParams;", null, null),
|
||||||
|
),
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC.value,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(4),
|
||||||
|
).toMutable().apply {
|
||||||
|
addInstructions(
|
||||||
|
"""
|
||||||
|
invoke-super { p0, p1, p2, p3 }, Landroid/view/ViewGroup;->addView(Landroid/view/View;ILandroid/view/ViewGroup${'$'}LayoutParams;)V
|
||||||
|
invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->playerOverlayGroupCreated(Landroid/view/View;)V
|
||||||
|
return-void
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Method.findReturnIndicesReversed() = findOpcodeIndicesReversed(Opcode.RETURN)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an override to force legacy tablet miniplayer to be used or not used.
|
||||||
|
*/
|
||||||
|
private fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) {
|
||||||
|
insertBooleanOverride(index, "getLegacyTabletMiniplayerOverride")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an override to force modern miniplayer to be used or not used.
|
||||||
|
*/
|
||||||
|
private fun MutableMethod.insertModernMiniplayerOverride(index: Int) {
|
||||||
|
insertBooleanOverride(index, "getModernMiniplayerOverride")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) {
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
addInstructions(
|
||||||
|
index,
|
||||||
|
"""
|
||||||
|
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Z)Z
|
||||||
|
move-result v$register
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an override to specify which modern miniplayer is used.
|
||||||
|
*/
|
||||||
|
private fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
|
||||||
|
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
|
||||||
|
val targetReference = (targetInstruction as ReferenceInstruction).reference
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
iPutIndex + 1, """
|
||||||
|
invoke-static { v${targetInstruction.registerA} }, $INTEGRATIONS_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
|
||||||
|
move-result v${targetInstruction.registerA}
|
||||||
|
# Original instruction
|
||||||
|
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
removeInstruction(iPutIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LiteralValueFingerprint.hookInflatedView(
|
||||||
|
literalValue: Long,
|
||||||
|
hookedClassType: String,
|
||||||
|
integrationsMethodName: String,
|
||||||
|
) {
|
||||||
|
resultOrThrow().mutableMethod.apply {
|
||||||
|
val imageViewIndex = indexOfFirstInstructionOrThrow(
|
||||||
|
indexOfFirstWideLiteralInstructionValueOrThrow(literalValue)
|
||||||
|
) {
|
||||||
|
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type == hookedClassType
|
||||||
|
}
|
||||||
|
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
|
||||||
|
addInstruction(
|
||||||
|
imageViewIndex + 1,
|
||||||
|
"invoke-static { v$register }, $integrationsMethodName"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||||
|
|
||||||
|
@Patch(dependencies = [ResourceMappingPatch::class])
|
||||||
|
internal object MiniplayerResourcePatch : ResourcePatch() {
|
||||||
|
var floatyBarButtonTopMargin = -1L
|
||||||
|
|
||||||
|
// Only available in 19.15 and upwards.
|
||||||
|
var ytOutlineXWhite24 = -1L
|
||||||
|
var ytOutlinePictureInPictureWhite24 = -1L
|
||||||
|
var scrimOverlay = -1L
|
||||||
|
var modernMiniplayerClose = -1L
|
||||||
|
var modernMiniplayerExpand = -1L
|
||||||
|
var modernMiniplayerRewindButton = -1L
|
||||||
|
var modernMiniplayerForwardButton = -1L
|
||||||
|
var playerOverlays = -1L
|
||||||
|
|
||||||
|
override fun execute(context: ResourceContext) {
|
||||||
|
floatyBarButtonTopMargin = ResourceMappingPatch[
|
||||||
|
"dimen",
|
||||||
|
"floaty_bar_button_top_margin"
|
||||||
|
]
|
||||||
|
|
||||||
|
try {
|
||||||
|
ytOutlinePictureInPictureWhite24 = ResourceMappingPatch[
|
||||||
|
"drawable",
|
||||||
|
"yt_outline_picture_in_picture_white_24"
|
||||||
|
]
|
||||||
|
} catch (exception: PatchException) {
|
||||||
|
// Ignore, and assume the app is 19.14 or earlier.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ytOutlineXWhite24 = ResourceMappingPatch[
|
||||||
|
"drawable",
|
||||||
|
"yt_outline_x_white_24"
|
||||||
|
]
|
||||||
|
|
||||||
|
scrimOverlay = ResourceMappingPatch[
|
||||||
|
"id",
|
||||||
|
"scrim_overlay"
|
||||||
|
]
|
||||||
|
|
||||||
|
modernMiniplayerClose = ResourceMappingPatch[
|
||||||
|
"id",
|
||||||
|
"modern_miniplayer_close"
|
||||||
|
]
|
||||||
|
|
||||||
|
modernMiniplayerExpand = ResourceMappingPatch[
|
||||||
|
"id",
|
||||||
|
"modern_miniplayer_expand"
|
||||||
|
]
|
||||||
|
|
||||||
|
modernMiniplayerRewindButton = ResourceMappingPatch[
|
||||||
|
"id",
|
||||||
|
"modern_miniplayer_rewind_button"
|
||||||
|
]
|
||||||
|
|
||||||
|
modernMiniplayerForwardButton = ResourceMappingPatch[
|
||||||
|
"id",
|
||||||
|
"modern_miniplayer_forward_button"
|
||||||
|
]
|
||||||
|
|
||||||
|
playerOverlays = ResourceMappingPatch[
|
||||||
|
"layout",
|
||||||
|
"player_overlays"
|
||||||
|
]
|
||||||
|
|
||||||
|
// Resource id is not used during patching, but is used by integrations.
|
||||||
|
// Verify the resource is present while patching.
|
||||||
|
ResourceMappingPatch[
|
||||||
|
"id",
|
||||||
|
"modern_miniplayer_subtitle_text"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object MiniplayerDimensionsCalculatorParentFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf("L"),
|
||||||
|
literalSupplier = { MiniplayerResourcePatch.floatyBarButtonTopMargin }
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
|
||||||
|
*/
|
||||||
|
internal object MiniplayerModernAddViewListenerFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf("Landroid/view/View;")
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
|
||||||
|
*/
|
||||||
|
internal object MiniplayerModernCloseButtonFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "Landroid/widget/ImageView;",
|
||||||
|
parameters = listOf(),
|
||||||
|
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerClose }
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object MiniplayerModernConstructorFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
parameters = listOf("L"),
|
||||||
|
literalSupplier = { 45623000L } // Magic number found in the constructor.
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
|
||||||
|
*/
|
||||||
|
internal object MiniplayerModernExpandButtonFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "Landroid/widget/ImageView;",
|
||||||
|
parameters = listOf(),
|
||||||
|
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerExpand }
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
|
||||||
|
*/
|
||||||
|
internal object MiniplayerModernExpandCloseDrawablesFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf("L"),
|
||||||
|
literalSupplier = { MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24 }
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
|
||||||
|
*/
|
||||||
|
internal object MiniplayerModernForwardButtonFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "Landroid/widget/ImageView;",
|
||||||
|
parameters = listOf(),
|
||||||
|
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerForwardButton }
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
|
||||||
|
*/
|
||||||
|
internal object MiniplayerModernOverlayViewFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf(),
|
||||||
|
literalSupplier = { MiniplayerResourcePatch.scrimOverlay }
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
|
||||||
|
*/
|
||||||
|
internal object MiniplayerModernRewindButtonFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "Landroid/widget/ImageView;",
|
||||||
|
parameters = listOf(),
|
||||||
|
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerRewindButton }
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object MiniplayerModernViewParentFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
parameters = listOf(),
|
||||||
|
strings = listOf("player_overlay_modern_mini_player_controls")
|
||||||
|
)
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
internal object MiniPlayerOverrideFingerprint : MethodFingerprint(
|
internal object MiniplayerOverrideFingerprint : MethodFingerprint(
|
||||||
returnType = "L",
|
returnType = "L",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
parameters = listOf("L"),
|
parameters = listOf("L"),
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.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 MiniplayerOverrideNoContextFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||||
|
returnType = "Z",
|
||||||
|
opcodes = listOf(Opcode.IGET_BOOLEAN), // anchor to insert the instruction
|
||||||
|
)
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal object MiniPlayerResponseModelSizeCheckFingerprint : MethodFingerprint(
|
internal object MiniplayerResponseModelSizeCheckFingerprint : MethodFingerprint(
|
||||||
"L",
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
returnType = "L",
|
||||||
listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
|
parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
|
||||||
listOf(
|
opcodes = listOf(
|
||||||
Opcode.RETURN_OBJECT,
|
Opcode.RETURN_OBJECT,
|
||||||
Opcode.CHECK_CAST,
|
Opcode.CHECK_CAST,
|
||||||
Opcode.CHECK_CAST,
|
Opcode.CHECK_CAST,
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
|
||||||
|
|
||||||
|
internal object YouTubePlayerOverlaysLayoutFingerprint : MethodFingerprint(
|
||||||
|
customFingerprint = { _, classDef ->
|
||||||
|
classDef.type == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
|
||||||
|
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package app.revanced.patches.youtube.layout.player.overlay
|
package app.revanced.patches.youtube.layout.player.overlay
|
||||||
|
|
||||||
import app.revanced.util.exception
|
|
||||||
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
@@ -9,6 +7,8 @@ import app.revanced.patcher.patch.BytecodePatch
|
|||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.youtube.layout.player.overlay.fingerprints.CreatePlayerOverviewFingerprint
|
import app.revanced.patches.youtube.layout.player.overlay.fingerprints.CreatePlayerOverviewFingerprint
|
||||||
|
import app.revanced.util.exception
|
||||||
|
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
@@ -27,7 +27,7 @@ object CustomPlayerOverlayOpacityPatch : BytecodePatch(setOf(CreatePlayerOvervie
|
|||||||
CreatePlayerOverviewFingerprint.result?.let { result ->
|
CreatePlayerOverviewFingerprint.result?.let { result ->
|
||||||
result.mutableMethod.apply {
|
result.mutableMethod.apply {
|
||||||
val viewRegisterIndex =
|
val viewRegisterIndex =
|
||||||
indexOfFirstWideLiteralInstructionValue(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3
|
indexOfFirstWideLiteralInstructionValueOrThrow(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3
|
||||||
val viewRegister =
|
val viewRegister =
|
||||||
getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA
|
getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package app.revanced.patches.youtube.layout.seekbar
|
package app.revanced.patches.youtube.layout.seekbar
|
||||||
|
|
||||||
import app.revanced.util.exception
|
|
||||||
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
@@ -15,6 +13,8 @@ import app.revanced.patches.youtube.layout.seekbar.fingerprints.ShortsSeekbarCol
|
|||||||
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch
|
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch
|
||||||
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook
|
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
|
import app.revanced.util.exception
|
||||||
|
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
|
|||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
|
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
|
||||||
val registerIndex = indexOfFirstWideLiteralInstructionValue(resourceId) + 2
|
val registerIndex = indexOfFirstWideLiteralInstructionValueOrThrow(resourceId) + 2
|
||||||
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
|
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
|
||||||
addInstructions(
|
addInstructions(
|
||||||
registerIndex + 1,
|
registerIndex + 1,
|
||||||
|
|||||||
@@ -13,18 +13,51 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|||||||
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
|
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.resultOrThrow
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Enable tablet layout",
|
name = "Enable tablet layout",
|
||||||
description = "Adds an option to spoof the device form factor to a tablet which enables the tablet layout.",
|
description = "Adds an option to enable tablet layout",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
|
dependencies = [
|
||||||
compatiblePackages = [CompatiblePackage("com.google.android.youtube")]
|
IntegrationsPatch::class,
|
||||||
|
SettingsPatch::class,
|
||||||
|
AddResourcesPatch::class,
|
||||||
|
],
|
||||||
|
compatiblePackages = [
|
||||||
|
CompatiblePackage(
|
||||||
|
"com.google.android.youtube", arrayOf(
|
||||||
|
"18.32.39",
|
||||||
|
"18.37.36",
|
||||||
|
"18.38.44",
|
||||||
|
"18.43.45",
|
||||||
|
"18.44.41",
|
||||||
|
"18.45.43",
|
||||||
|
"18.48.39",
|
||||||
|
"18.49.37",
|
||||||
|
"19.01.34",
|
||||||
|
"19.02.39",
|
||||||
|
"19.03.36",
|
||||||
|
"19.04.38",
|
||||||
|
"19.05.36",
|
||||||
|
"19.06.39",
|
||||||
|
"19.07.40",
|
||||||
|
"19.08.36",
|
||||||
|
"19.09.38",
|
||||||
|
"19.10.39",
|
||||||
|
"19.11.43",
|
||||||
|
"19.12.41",
|
||||||
|
"19.13.37",
|
||||||
|
"19.14.43",
|
||||||
|
"19.15.36",
|
||||||
|
"19.16.39"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object EnableTabletLayoutPatch : BytecodePatch(
|
object EnableTabletLayoutPatch : BytecodePatch(setOf(GetFormFactorFingerprint)) {
|
||||||
setOf(GetFormFactorFingerprint)
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/TabletLayoutPatch;"
|
||||||
) {
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
AddResourcesPatch(this::class)
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
@@ -32,7 +65,7 @@ object EnableTabletLayoutPatch : BytecodePatch(
|
|||||||
SwitchPreference("revanced_tablet_layout")
|
SwitchPreference("revanced_tablet_layout")
|
||||||
)
|
)
|
||||||
|
|
||||||
GetFormFactorFingerprint.result?.let {
|
GetFormFactorFingerprint.resultOrThrow().let {
|
||||||
it.mutableMethod.apply {
|
it.mutableMethod.apply {
|
||||||
val returnIsLargeFormFactorIndex = getInstructions().lastIndex - 4
|
val returnIsLargeFormFactorIndex = getInstructions().lastIndex - 4
|
||||||
val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex)
|
val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex)
|
||||||
@@ -40,8 +73,8 @@ object EnableTabletLayoutPatch : BytecodePatch(
|
|||||||
addInstructionsWithLabels(
|
addInstructionsWithLabels(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
invoke-static { }, Lapp/revanced/integrations/youtube/patches/EnableTabletLayoutPatch;->enableTabletLayout()Z
|
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getTabletLayoutEnabled()Z
|
||||||
move-result v0 # Free register
|
move-result v0
|
||||||
if-nez v0, :is_large_form_factor
|
if-nez v0, :is_large_form_factor
|
||||||
""",
|
""",
|
||||||
ExternalLabel(
|
ExternalLabel(
|
||||||
@@ -50,6 +83,6 @@ object EnableTabletLayoutPatch : BytecodePatch(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: GetFormFactorFingerprint.exception
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,5 +21,6 @@ internal object GetFormFactorFingerprint : MethodFingerprint(
|
|||||||
Opcode.INVOKE_STATIC,
|
Opcode.INVOKE_STATIC,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.RETURN_OBJECT
|
Opcode.RETURN_OBJECT
|
||||||
)
|
),
|
||||||
|
strings = listOf("")
|
||||||
)
|
)
|
||||||
@@ -1,152 +1,11 @@
|
|||||||
package app.revanced.patches.youtube.layout.tabletminiplayer
|
package app.revanced.patches.youtube.layout.tabletminiplayer
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerPatch
|
||||||
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.all.misc.resources.AddResourcesPatch
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|
||||||
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerDimensionsCalculatorParentFingerprint
|
|
||||||
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideFingerprint
|
|
||||||
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideNoContextFingerprint
|
|
||||||
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerResponseModelSizeCheckFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
|
||||||
import app.revanced.util.exception
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
|
|
||||||
@Patch(
|
@Deprecated("This patch class has been renamed to Miniplayer.")
|
||||||
name = "Tablet mini player",
|
object TabletMiniPlayerPatch : BytecodePatch(dependencies = setOf(MiniplayerPatch::class)) {
|
||||||
description = "Adds an option to enable the tablet mini player layout.",
|
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
|
|
||||||
compatiblePackages = [
|
|
||||||
CompatiblePackage(
|
|
||||||
"com.google.android.youtube", arrayOf(
|
|
||||||
"18.32.39",
|
|
||||||
"18.37.36",
|
|
||||||
"18.38.44",
|
|
||||||
"18.43.45",
|
|
||||||
"18.44.41",
|
|
||||||
"18.45.43",
|
|
||||||
"18.48.39",
|
|
||||||
"18.49.37",
|
|
||||||
"19.01.34",
|
|
||||||
"19.02.39",
|
|
||||||
"19.03.36",
|
|
||||||
"19.04.38",
|
|
||||||
"19.05.36",
|
|
||||||
"19.06.39",
|
|
||||||
"19.07.40",
|
|
||||||
"19.08.36",
|
|
||||||
"19.09.38",
|
|
||||||
"19.10.39",
|
|
||||||
"19.11.43",
|
|
||||||
"19.12.41",
|
|
||||||
"19.13.37",
|
|
||||||
"19.14.43",
|
|
||||||
"19.15.36",
|
|
||||||
"19.16.39",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@Suppress("unused")
|
|
||||||
object TabletMiniPlayerPatch : BytecodePatch(
|
|
||||||
setOf(
|
|
||||||
MiniPlayerDimensionsCalculatorParentFingerprint,
|
|
||||||
MiniPlayerResponseModelSizeCheckFingerprint,
|
|
||||||
MiniPlayerOverrideFingerprint
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
AddResourcesPatch(this::class)
|
|
||||||
|
|
||||||
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
|
||||||
SwitchPreference("revanced_tablet_miniplayer")
|
|
||||||
)
|
|
||||||
|
|
||||||
// First resolve the fingerprints via the parent fingerprint.
|
|
||||||
MiniPlayerDimensionsCalculatorParentFingerprint.result
|
|
||||||
?: throw MiniPlayerDimensionsCalculatorParentFingerprint.exception
|
|
||||||
val miniPlayerClass = MiniPlayerDimensionsCalculatorParentFingerprint.result!!.classDef
|
|
||||||
|
|
||||||
/*
|
|
||||||
* No context parameter method.
|
|
||||||
*/
|
|
||||||
MiniPlayerOverrideNoContextFingerprint.resolve(context, miniPlayerClass)
|
|
||||||
val (method, _, parameterRegister) = MiniPlayerOverrideNoContextFingerprint.addProxyCall()
|
|
||||||
|
|
||||||
// Insert right before the return instruction.
|
|
||||||
val secondInsertIndex = method.implementation!!.instructions.size - 1
|
|
||||||
method.insertOverride(
|
|
||||||
secondInsertIndex, parameterRegister
|
|
||||||
/** same register used to return **/
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Override every return instruction with the proxy call.
|
|
||||||
*/
|
|
||||||
MiniPlayerOverrideFingerprint.result?.let { result ->
|
|
||||||
result.mutableMethod.let { method ->
|
|
||||||
val appNameStringIndex = result.scanResult.stringsScanResult!!.matches.first().index + 2
|
|
||||||
context.toMethodWalker(method).nextMethod(appNameStringIndex, true)
|
|
||||||
.getMethod() as MutableMethod
|
|
||||||
}.apply {
|
|
||||||
implementation!!.let { implementation ->
|
|
||||||
val returnIndices = implementation.instructions
|
|
||||||
.withIndex()
|
|
||||||
.filter { (_, instruction) -> instruction.opcode == Opcode.RETURN }
|
|
||||||
.map { (index, _) -> index }
|
|
||||||
|
|
||||||
if (returnIndices.isEmpty()) throw PatchException("No return instructions found.")
|
|
||||||
|
|
||||||
// This method clobbers register p0 to return the value, calculate to override.
|
|
||||||
val returnedRegister = implementation.registerCount - parameters.size
|
|
||||||
|
|
||||||
// Hook the returned register on every return instruction.
|
|
||||||
returnIndices.forEach { index -> insertOverride(index, returnedRegister) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return@let
|
|
||||||
} ?: throw MiniPlayerOverrideFingerprint.exception
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Size check return value override.
|
|
||||||
*/
|
|
||||||
MiniPlayerResponseModelSizeCheckFingerprint.addProxyCall()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods.
|
|
||||||
private fun MethodFingerprint.addProxyCall(): Triple<MutableMethod, Int, Int> {
|
|
||||||
val (method, scanIndex, parameterRegister) = this.unwrap()
|
|
||||||
method.insertOverride(scanIndex, parameterRegister)
|
|
||||||
|
|
||||||
return Triple(method, scanIndex, parameterRegister)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun MutableMethod.insertOverride(index: Int, overrideRegister: Int) {
|
|
||||||
this.addInstructions(
|
|
||||||
index,
|
|
||||||
"""
|
|
||||||
invoke-static {v$overrideRegister}, Lapp/revanced/integrations/youtube/patches/TabletMiniPlayerOverridePatch;->getTabletMiniPlayerOverride(Z)Z
|
|
||||||
move-result v$overrideRegister
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun MethodFingerprint.unwrap(): Triple<MutableMethod, Int, Int> {
|
|
||||||
val result = this.result!!
|
|
||||||
val scanIndex = result.scanResult.patternScanResult!!.endIndex
|
|
||||||
val method = result.mutableMethod
|
|
||||||
val instructions = method.implementation!!.instructions
|
|
||||||
val parameterRegister = (instructions[scanIndex] as OneRegisterInstruction).registerA
|
|
||||||
|
|
||||||
return Triple(method, scanIndex, parameterRegister)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.layout.tabletminiplayer.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 MiniPlayerDimensionsCalculatorParentFingerprint : MethodFingerprint(
|
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
returnType = "V",
|
|
||||||
parameters = listOf("F"),
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.CONST_HIGH16,
|
|
||||||
Opcode.ADD_FLOAT_2ADDR,
|
|
||||||
null, // Opcode.MUL_FLOAT or Opcode.MUL_FLOAT_2ADDR
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.FLOAT_TO_INT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.RETURN_VOID,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.layout.tabletminiplayer.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 MiniPlayerOverrideNoContextFingerprint : MethodFingerprint(
|
|
||||||
"Z", AccessFlags.FINAL or AccessFlags.PRIVATE,
|
|
||||||
opcodes = listOf(Opcode.RETURN), // anchor to insert the instruction
|
|
||||||
)
|
|
||||||
@@ -16,7 +16,7 @@ import app.revanced.patches.youtube.layout.theme.fingerprints.UseGradientLoading
|
|||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
|
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
|
||||||
import app.revanced.util.resultOrThrow
|
import app.revanced.util.resultOrThrow
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ object ThemeBytecodePatch : BytecodePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
UseGradientLoadingScreenFingerprint.result?.mutableMethod?.apply {
|
UseGradientLoadingScreenFingerprint.result?.mutableMethod?.apply {
|
||||||
val isEnabledIndex = indexOfFirstWideLiteralInstructionValue(GRADIENT_LOADING_SCREEN_AB_CONSTANT) + 3
|
val isEnabledIndex = indexOfFirstWideLiteralInstructionValueOrThrow(GRADIENT_LOADING_SCREEN_AB_CONSTANT) + 3
|
||||||
val isEnabledRegister = getInstruction<OneRegisterInstruction>(isEnabledIndex - 1).registerA
|
val isEnabledRegister = getInstruction<OneRegisterInstruction>(isEnabledIndex - 1).registerA
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.backgroundplayback
|
||||||
|
|
||||||
|
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.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||||
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackManagerFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackSettingsFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
|
import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
name = "Remove background playback restrictions",
|
||||||
|
description = "Removes restrictions on background playback, including playing kids videos in the background.",
|
||||||
|
dependencies = [
|
||||||
|
BackgroundPlaybackResourcePatch::class,
|
||||||
|
IntegrationsPatch::class,
|
||||||
|
PlayerTypeHookPatch::class,
|
||||||
|
VideoInformationPatch::class,
|
||||||
|
SettingsPatch::class,
|
||||||
|
AddResourcesPatch::class
|
||||||
|
],
|
||||||
|
compatiblePackages = [
|
||||||
|
CompatiblePackage(
|
||||||
|
"com.google.android.youtube",
|
||||||
|
[
|
||||||
|
"18.48.39",
|
||||||
|
"18.49.37",
|
||||||
|
"19.01.34",
|
||||||
|
"19.02.39",
|
||||||
|
"19.03.36",
|
||||||
|
"19.04.38",
|
||||||
|
"19.05.36",
|
||||||
|
"19.06.39",
|
||||||
|
"19.07.40",
|
||||||
|
"19.08.36",
|
||||||
|
"19.09.38",
|
||||||
|
"19.10.39",
|
||||||
|
"19.11.43",
|
||||||
|
"19.12.41",
|
||||||
|
"19.13.37",
|
||||||
|
"19.14.43",
|
||||||
|
"19.15.36",
|
||||||
|
"19.16.39",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object BackgroundPlaybackPatch : BytecodePatch(
|
||||||
|
setOf(
|
||||||
|
BackgroundPlaybackManagerFingerprint,
|
||||||
|
BackgroundPlaybackSettingsFingerprint,
|
||||||
|
KidsBackgroundPlaybackPolicyControllerFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/integrations/youtube/patches/BackgroundPlaybackPatch;"
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
|
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||||
|
NonInteractivePreference("revanced_background_playback")
|
||||||
|
)
|
||||||
|
|
||||||
|
BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z
|
||||||
|
move-result v0
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enable background playback option in YouTube settings
|
||||||
|
BackgroundPlaybackSettingsFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
val booleanCalls = implementation!!.instructions.withIndex()
|
||||||
|
.filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" }
|
||||||
|
|
||||||
|
val settingsBooleanIndex = booleanCalls.elementAt(1).index
|
||||||
|
val settingsBooleanMethod =
|
||||||
|
context.toMethodWalker(this).nextMethod(settingsBooleanIndex, true).getMethod() as MutableMethod
|
||||||
|
|
||||||
|
settingsBooleanMethod.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideBackgroundPlaybackAvailable()Z
|
||||||
|
move-result v0
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force allowing background play for videos labeled for kids.
|
||||||
|
KidsBackgroundPlaybackPolicyControllerFingerprint.resultOrThrow().mutableMethod.addInstruction(
|
||||||
|
0,
|
||||||
|
"return-void"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.patches.youtube.misc.minimizedplayback
|
package app.revanced.patches.youtube.misc.backgroundplayback
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
@@ -8,7 +8,7 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
|||||||
@Patch(
|
@Patch(
|
||||||
dependencies = [ResourceMappingPatch::class],
|
dependencies = [ResourceMappingPatch::class],
|
||||||
)
|
)
|
||||||
internal object MinimizedPlaybackResourcePatch : ResourcePatch() {
|
internal object BackgroundPlaybackResourcePatch : ResourcePatch() {
|
||||||
internal var prefBackgroundAndOfflineCategoryId: Long = -1
|
internal var prefBackgroundAndOfflineCategoryId: Long = -1
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
|
package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
|
internal object BackgroundPlaybackManagerFingerprint : MethodFingerprint(
|
||||||
"Z",
|
"Z",
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
listOf("L"),
|
listOf("L"),
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
|
package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patches.youtube.misc.minimizedplayback.MinimizedPlaybackResourcePatch
|
import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackResourcePatch
|
||||||
import app.revanced.util.patch.LiteralValueFingerprint
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal object MinimizedPlaybackSettingsFingerprint : LiteralValueFingerprint(
|
internal object BackgroundPlaybackSettingsFingerprint : LiteralValueFingerprint(
|
||||||
returnType = "Ljava/lang/String;",
|
returnType = "Ljava/lang/String;",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
parameters = listOf(),
|
parameters = listOf(),
|
||||||
@@ -19,5 +19,5 @@ internal object MinimizedPlaybackSettingsFingerprint : LiteralValueFingerprint(
|
|||||||
Opcode.IF_NEZ,
|
Opcode.IF_NEZ,
|
||||||
Opcode.GOTO
|
Opcode.GOTO
|
||||||
),
|
),
|
||||||
literalSupplier = { MinimizedPlaybackResourcePatch.prefBackgroundAndOfflineCategoryId }
|
literalSupplier = { BackgroundPlaybackResourcePatch.prefBackgroundAndOfflineCategoryId }
|
||||||
)
|
)
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
|
package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
|
|
||||||
|
|
||||||
internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint(
|
internal object KidsBackgroundPlaybackPolicyControllerFingerprint : LiteralValueFingerprint(
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
parameters = listOf("I", "L", "L"),
|
parameters = listOf("I", "L", "L"),
|
||||||
@@ -26,9 +25,5 @@ internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerp
|
|||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.RETURN_VOID
|
Opcode.RETURN_VOID
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef, _ ->
|
literalSupplier = { 5 },
|
||||||
methodDef.implementation!!.instructions.any {
|
|
||||||
((it as? NarrowLiteralInstruction)?.narrowLiteral == 5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
@@ -14,14 +14,8 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
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
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlaybackRequestFingerprint
|
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildPlayerRequestURIFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyWithModelFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.SetPlayerRequestClientTypeFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
@@ -86,6 +80,9 @@ object SpoofClientPatch : BytecodePatch(
|
|||||||
|
|
||||||
// Player gesture config.
|
// Player gesture config.
|
||||||
PlayerGestureConfigSyntheticFingerprint,
|
PlayerGestureConfigSyntheticFingerprint,
|
||||||
|
|
||||||
|
// Player speed menu item.
|
||||||
|
CreatePlaybackSpeedMenuItemFingerprint,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
@@ -99,7 +96,7 @@ object SpoofClientPatch : BytecodePatch(
|
|||||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||||
PreferenceScreen(
|
PreferenceScreen(
|
||||||
key = "revanced_spoof_client_screen",
|
key = "revanced_spoof_client_screen",
|
||||||
sorting = Sorting.UNSORTED,
|
sorting = PreferenceScreen.Sorting.UNSORTED,
|
||||||
preferences = setOf(
|
preferences = setOf(
|
||||||
SwitchPreference("revanced_spoof_client"),
|
SwitchPreference("revanced_spoof_client"),
|
||||||
SwitchPreference("revanced_spoof_client_use_ios"),
|
SwitchPreference("revanced_spoof_client_use_ios"),
|
||||||
@@ -127,33 +124,6 @@ object SpoofClientPatch : BytecodePatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region fix player gesture.
|
|
||||||
|
|
||||||
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
|
|
||||||
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
|
||||||
|
|
||||||
arrayOf(3, 9).forEach { offSet ->
|
|
||||||
(context.toMethodWalker(it.mutableMethod)
|
|
||||||
.nextMethod(endIndex - offSet, true)
|
|
||||||
.getMethod() as MutableMethod)
|
|
||||||
.apply {
|
|
||||||
|
|
||||||
val index = implementation!!.instructions.lastIndex
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
index,
|
|
||||||
"""
|
|
||||||
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
|
|
||||||
move-result v$register
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Block /get_watch requests to fall back to /player requests.
|
// region Block /get_watch requests to fall back to /player requests.
|
||||||
|
|
||||||
BuildPlayerRequestURIFingerprint.resultOrThrow().let {
|
BuildPlayerRequestURIFingerprint.resultOrThrow().let {
|
||||||
@@ -281,5 +251,59 @@ object SpoofClientPatch : BytecodePatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region Fix player gesture if spoofing to iOS.
|
||||||
|
|
||||||
|
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
|
||||||
|
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
|
val downAndOutLandscapeAllowedIndex = endIndex - 3
|
||||||
|
val downAndOutPortraitAllowedIndex = endIndex - 9
|
||||||
|
|
||||||
|
arrayOf(
|
||||||
|
downAndOutLandscapeAllowedIndex,
|
||||||
|
downAndOutPortraitAllowedIndex,
|
||||||
|
).forEach { index ->
|
||||||
|
val gestureAllowedMethod = context.toMethodWalker(it.mutableMethod)
|
||||||
|
.nextMethod(index, true)
|
||||||
|
.getMethod() as MutableMethod
|
||||||
|
|
||||||
|
gestureAllowedMethod.apply {
|
||||||
|
val isAllowedIndex = getInstructions().lastIndex
|
||||||
|
val isAllowed = getInstruction<OneRegisterInstruction>(isAllowedIndex).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
isAllowedIndex,
|
||||||
|
"""
|
||||||
|
invoke-static { v$isAllowed }, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
|
||||||
|
move-result v$isAllowed
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// Fix playback speed menu item if spoofing to iOS.
|
||||||
|
|
||||||
|
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
|
||||||
|
val scanResult = it.scanResult.patternScanResult!!
|
||||||
|
if (scanResult.startIndex != 0) throw PatchException("Unexpected start index: ${scanResult.startIndex}")
|
||||||
|
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
// Find the conditional check if the playback speed menu item is not created.
|
||||||
|
val shouldCreateMenuIndex = indexOfFirstInstructionOrThrow(scanResult.endIndex) { opcode == Opcode.IF_EQZ }
|
||||||
|
val shouldCreateMenuRegister = getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
shouldCreateMenuIndex,
|
||||||
|
"""
|
||||||
|
invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z
|
||||||
|
move-result v$shouldCreateMenuRegister
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.fix.playback.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 CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.IGET_OBJECT, // First instruction of the method
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
null // MOVE_RESULT or MOVE_RESULT_OBJECT, Return value controls the creation of the playback speed menu item.
|
||||||
|
),
|
||||||
|
// 19.01 and earlier is missing the second parameter.
|
||||||
|
// Since this fingerprint is somewhat weak, work around by checking for both method parameter signatures.
|
||||||
|
customFingerprint = custom@{ methodDef, _ ->
|
||||||
|
// 19.01 and earlier parameters are: "[L"
|
||||||
|
// 19.02+ parameters are "[L", "F"
|
||||||
|
val parameterTypes = methodDef.parameterTypes
|
||||||
|
val firstParameter = parameterTypes.firstOrNull()
|
||||||
|
|
||||||
|
if (firstParameter == null || !firstParameter.startsWith("[L")) {
|
||||||
|
return@custom false
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterTypes.size == 1 || (parameterTypes.size == 2 && parameterTypes[1] == "F")
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint.indexOfDownAndOutAllowedInstruction
|
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstruction
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
@@ -24,28 +22,28 @@ internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
|
|||||||
Opcode.IGET_OBJECT,
|
Opcode.IGET_OBJECT,
|
||||||
Opcode.INVOKE_INTERFACE,
|
Opcode.INVOKE_INTERFACE,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed
|
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed.
|
||||||
Opcode.MOVE_RESULT,
|
Opcode.MOVE_RESULT,
|
||||||
Opcode.CHECK_CAST,
|
Opcode.CHECK_CAST,
|
||||||
Opcode.IPUT_BOOLEAN,
|
Opcode.IPUT_BOOLEAN,
|
||||||
Opcode.INVOKE_INTERFACE,
|
Opcode.INVOKE_INTERFACE,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed
|
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed.
|
||||||
Opcode.MOVE_RESULT,
|
Opcode.MOVE_RESULT,
|
||||||
Opcode.IPUT_BOOLEAN,
|
Opcode.IPUT_BOOLEAN,
|
||||||
Opcode.RETURN_VOID,
|
Opcode.RETURN_VOID,
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef, classDef ->
|
customFingerprint = { methodDef, classDef ->
|
||||||
// This method is always called "a" because this kind of class always has a single method.
|
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
|
||||||
methodDef.name == "a" && classDef.methods.count() == 2 &&
|
methodDef.indexOfFirstInstruction {
|
||||||
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
|
val reference = getReference<MethodReference>()
|
||||||
}
|
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
|
||||||
) {
|
|
||||||
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
|
|
||||||
methodDef.indexOfFirstInstruction {
|
|
||||||
val reference = getReference<MethodReference>()
|
|
||||||
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
|
|
||||||
reference.parameterTypes.isEmpty() &&
|
reference.parameterTypes.isEmpty() &&
|
||||||
reference.returnType == "Z"
|
reference.returnType == "Z"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// This method is always called "a" because this kind of class always has a single method.
|
||||||
|
methodDef.name == "a" && classDef.methods.count() == 2 &&
|
||||||
|
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,117 +1,11 @@
|
|||||||
package app.revanced.patches.youtube.misc.minimizedplayback
|
package app.revanced.patches.youtube.misc.minimizedplayback
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
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.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackPatch
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|
||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
|
||||||
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsFingerprint
|
|
||||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
|
||||||
import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
|
||||||
import app.revanced.util.exception
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
|
|
||||||
@Patch(
|
|
||||||
name = "Minimized playback",
|
|
||||||
description = "Unlocks options for picture-in-picture and background playback.",
|
|
||||||
dependencies = [
|
|
||||||
MinimizedPlaybackResourcePatch::class,
|
|
||||||
IntegrationsPatch::class,
|
|
||||||
PlayerTypeHookPatch::class,
|
|
||||||
VideoInformationPatch::class,
|
|
||||||
SettingsPatch::class,
|
|
||||||
AddResourcesPatch::class
|
|
||||||
],
|
|
||||||
compatiblePackages = [
|
|
||||||
CompatiblePackage(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
[
|
|
||||||
"18.48.39",
|
|
||||||
"18.49.37",
|
|
||||||
"19.01.34",
|
|
||||||
"19.02.39",
|
|
||||||
"19.03.36",
|
|
||||||
"19.04.38",
|
|
||||||
"19.05.36",
|
|
||||||
"19.06.39",
|
|
||||||
"19.07.40",
|
|
||||||
"19.08.36",
|
|
||||||
"19.09.38",
|
|
||||||
"19.10.39",
|
|
||||||
"19.11.43",
|
|
||||||
"19.12.41",
|
|
||||||
"19.13.37",
|
|
||||||
"19.14.43",
|
|
||||||
"19.15.36",
|
|
||||||
"19.16.39",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@Suppress("unused")
|
|
||||||
object MinimizedPlaybackPatch : BytecodePatch(
|
|
||||||
setOf(
|
|
||||||
MinimizedPlaybackManagerFingerprint,
|
|
||||||
MinimizedPlaybackSettingsFingerprint,
|
|
||||||
KidsMinimizedPlaybackPolicyControllerFingerprint
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
|
||||||
"Lapp/revanced/integrations/youtube/patches/MinimizedPlaybackPatch;"
|
|
||||||
|
|
||||||
|
@Deprecated("This patch class has been renamed to BackgroundPlaybackPatch.")
|
||||||
|
object MinimizedPlaybackPatch : BytecodePatch(dependencies = setOf(BackgroundPlaybackPatch::class)) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
AddResourcesPatch(this::class)
|
|
||||||
|
|
||||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
|
||||||
NonInteractivePreference("revanced_minimized_playback")
|
|
||||||
)
|
|
||||||
|
|
||||||
MinimizedPlaybackManagerFingerprint.result?.apply {
|
|
||||||
mutableMethod.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z
|
|
||||||
move-result v0
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
} ?: throw MinimizedPlaybackManagerFingerprint.exception
|
|
||||||
|
|
||||||
// Enable minimized playback option in YouTube settings
|
|
||||||
MinimizedPlaybackSettingsFingerprint.result?.apply {
|
|
||||||
val booleanCalls = method.implementation!!.instructions.withIndex()
|
|
||||||
.filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" }
|
|
||||||
|
|
||||||
val settingsBooleanIndex = booleanCalls.elementAt(1).index
|
|
||||||
val settingsBooleanMethod =
|
|
||||||
context.toMethodWalker(method).nextMethod(settingsBooleanIndex, true).getMethod() as MutableMethod
|
|
||||||
|
|
||||||
settingsBooleanMethod.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideMinimizedPlaybackAvailable()Z
|
|
||||||
move-result v0
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
} ?: throw MinimizedPlaybackSettingsFingerprint.exception
|
|
||||||
|
|
||||||
// Force allowing background play for videos labeled for kids.
|
|
||||||
// Some regions and YouTube accounts do not require this patch.
|
|
||||||
KidsMinimizedPlaybackPolicyControllerFingerprint.result?.apply {
|
|
||||||
mutableMethod.addInstruction(
|
|
||||||
0,
|
|
||||||
"return-void"
|
|
||||||
)
|
|
||||||
} ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,13 +4,19 @@ import app.revanced.patcher.data.BytecodeContext
|
|||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patches.youtube.video.speed.button.PlaybackSpeedButtonPatch
|
||||||
import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch
|
import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch
|
||||||
import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPatch
|
import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPatch
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Playback speed",
|
name = "Playback speed",
|
||||||
description = "Adds options to customize available playback speeds and to remember the last playback speed selected.",
|
description = "Adds options to customize available playback speeds, remember the last playback speed selected " +
|
||||||
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class],
|
"and show a speed dialog button to the video player.",
|
||||||
|
dependencies = [
|
||||||
|
PlaybackSpeedButtonPatch::class,
|
||||||
|
CustomPlaybackSpeedPatch::class,
|
||||||
|
RememberPlaybackSpeedPatch::class,
|
||||||
|
],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube",
|
"com.google.android.youtube",
|
||||||
@@ -42,4 +48,4 @@ object PlaybackSpeedPatch : BytecodePatch(emptySet()) {
|
|||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// All patches this patch depends on succeed.
|
// All patches this patch depends on succeed.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.patches.youtube.video.speed.button
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
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.playercontrols.PlayerControlsBytecodePatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
|
import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
description = "Adds the option to display playback speed dialog button in the video player.",
|
||||||
|
dependencies = [
|
||||||
|
PlaybackSpeedButtonResourcePatch::class,
|
||||||
|
CustomPlaybackSpeedPatch::class,
|
||||||
|
PlayerControlsBytecodePatch::class,
|
||||||
|
SettingsPatch::class,
|
||||||
|
AddResourcesPatch::class,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object PlaybackSpeedButtonPatch : BytecodePatch(emptySet()) {
|
||||||
|
private const val SPEED_BUTTON_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/integrations/youtube/videoplayer/PlaybackSpeedDialogButton;"
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
|
||||||
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
|
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
|
||||||
|
SwitchPreference("revanced_playback_speed_dialog_button"),
|
||||||
|
)
|
||||||
|
|
||||||
|
PlayerControlsBytecodePatch.initializeControl("$SPEED_BUTTON_CLASS_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
|
||||||
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$SPEED_BUTTON_CLASS_DESCRIPTOR->changeVisibility(Z)V")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package app.revanced.patches.youtube.video.speed.button
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
||||||
|
import app.revanced.util.ResourceGroup
|
||||||
|
import app.revanced.util.copyResources
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
dependencies = [BottomControlsResourcePatch::class],
|
||||||
|
)
|
||||||
|
internal object PlaybackSpeedButtonResourcePatch : ResourcePatch() {
|
||||||
|
override fun execute(context: ResourceContext) {
|
||||||
|
context.copyResources(
|
||||||
|
"speedbutton",
|
||||||
|
ResourceGroup(
|
||||||
|
"drawable",
|
||||||
|
"revanced_playback_speed_dialog_button.xml",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
BottomControlsResourcePatch.addControls("speedbutton")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import app.revanced.patcher.patch.PatchException
|
|||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
@@ -99,6 +100,7 @@ fun Method.indexOfIdResourceOrThrow(resourceName: String): Int {
|
|||||||
* Find the index of the first wide literal instruction with the given value.
|
* Find the index of the first wide literal instruction with the given value.
|
||||||
*
|
*
|
||||||
* @return the first literal instruction with the value, or -1 if not found.
|
* @return the first literal instruction with the value, or -1 if not found.
|
||||||
|
* @see indexOfFirstWideLiteralInstructionValueOrThrow
|
||||||
*/
|
*/
|
||||||
fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let {
|
fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let {
|
||||||
it.instructions.indexOfFirst { instruction ->
|
it.instructions.indexOfFirst { instruction ->
|
||||||
@@ -106,6 +108,18 @@ fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementati
|
|||||||
}
|
}
|
||||||
} ?: -1
|
} ?: -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the index of the first wide literal instruction with the given value,
|
||||||
|
* or throw an exception if not found.
|
||||||
|
*
|
||||||
|
* @return the first literal instruction with the value, or throws [PatchException] if not found.
|
||||||
|
*/
|
||||||
|
fun Method.indexOfFirstWideLiteralInstructionValueOrThrow(literal: Long) : Int {
|
||||||
|
val index = indexOfFirstWideLiteralInstructionValue(literal)
|
||||||
|
if (index < 0) throw PatchException("Could not find literal value: $literal")
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the method contains a literal with the given value.
|
* Check if the method contains a literal with the given value.
|
||||||
*
|
*
|
||||||
@@ -144,7 +158,9 @@ inline fun <reified T : Reference> Instruction.getReference() = (this as? Refere
|
|||||||
* @return The index of the first [Instruction] that matches the predicate.
|
* @return The index of the first [Instruction] that matches the predicate.
|
||||||
*/
|
*/
|
||||||
// TODO: delete this on next major release, the overloaded method with an optional start index serves the same purposes.
|
// TODO: delete this on next major release, the overloaded method with an optional start index serves the same purposes.
|
||||||
@Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)"))
|
// Method is deprecated, but annotation is commented out otherwise during compilation usage of the replacement is
|
||||||
|
// incorrectly flagged as deprecated.
|
||||||
|
//@Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)"))
|
||||||
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate)
|
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,6 +195,21 @@ fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, predicate: Instru
|
|||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The list of indices of the opcode in reverse order.
|
||||||
|
*/
|
||||||
|
fun Method.findOpcodeIndicesReversed(opcode: Opcode): List<Int> {
|
||||||
|
val indexes = implementation!!.instructions
|
||||||
|
.withIndex()
|
||||||
|
.filter { (_, instruction) -> instruction.opcode == opcode }
|
||||||
|
.map { (index, _) -> index }
|
||||||
|
.reversed()
|
||||||
|
|
||||||
|
if (indexes.isEmpty()) throw PatchException("No ${opcode.name} instructions found in: $this")
|
||||||
|
|
||||||
|
return indexes
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the resolved methods of [MethodFingerprint]s early.
|
* Return the resolved methods of [MethodFingerprint]s early.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,6 +16,35 @@
|
|||||||
<item>17.33.42</item>
|
<item>17.33.42</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="layout.miniplayer.MiniplayerPatch">
|
||||||
|
<string-array name="revanced_miniplayer_type_19_15_entries">
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_1</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_2</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_3</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_4</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_5</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_6</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_miniplayer_type_19_15_entry_values">
|
||||||
|
<!-- Enum names from Integrations. -->
|
||||||
|
<item>ORIGINAL</item>
|
||||||
|
<item>PHONE</item>
|
||||||
|
<item>TABLET</item>
|
||||||
|
<item>MODERN_1</item>
|
||||||
|
<item>MODERN_2</item>
|
||||||
|
<item>MODERN_3</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_miniplayer_type_legacy_entries">
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_1</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_2</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_3</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_miniplayer_type_legacy_entry_values">
|
||||||
|
<item>ORIGINAL</item>
|
||||||
|
<item>PHONE</item>
|
||||||
|
<item>TABLET</item>
|
||||||
|
</string-array>
|
||||||
|
</patch>
|
||||||
<patch id="layout.startpage.ChangeStartPagePatch">
|
<patch id="layout.startpage.ChangeStartPagePatch">
|
||||||
<string-array name="revanced_start_page_entries">
|
<string-array name="revanced_start_page_entries">
|
||||||
<item>@string/revanced_start_page_entry_0</item>
|
<item>@string/revanced_start_page_entry_0</item>
|
||||||
|
|||||||
@@ -661,6 +661,7 @@
|
|||||||
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
|
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
|
||||||
<string name="revanced_player_overlay_opacity_title">Player overlay opacity</string>
|
<string name="revanced_player_overlay_opacity_title">Player overlay opacity</string>
|
||||||
<string name="revanced_player_overlay_opacity_summary">Opacity value between 0-100, where 0 is transparent</string>
|
<string name="revanced_player_overlay_opacity_summary">Opacity value between 0-100, where 0 is transparent</string>
|
||||||
|
<string name="revanced_player_overlay_opacity_invalid_toast">Player overlay opacity must be between 0-100</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
||||||
<string name="revanced_ryd_settings_title">Return YouTube Dislike</string>
|
<string name="revanced_ryd_settings_title">Return YouTube Dislike</string>
|
||||||
@@ -947,11 +948,29 @@
|
|||||||
<string name="revanced_tablet_layout_summary_on">Tablet layout is enabled</string>
|
<string name="revanced_tablet_layout_summary_on">Tablet layout is enabled</string>
|
||||||
<string name="revanced_tablet_layout_summary_off">Tablet layout is disabled</string>
|
<string name="revanced_tablet_layout_summary_off">Tablet layout is disabled</string>
|
||||||
<string name="revanced_tablet_layout_user_dialog_message">Community posts do not show up on tablet layouts</string>
|
<string name="revanced_tablet_layout_user_dialog_message">Community posts do not show up on tablet layouts</string>
|
||||||
</patch>
|
</patch>x
|
||||||
<patch id="layout.tabletminiplayer.TabletMiniPlayerPatch">
|
<patch id="layout.miniplayer.MiniplayerPatch">
|
||||||
<string name="revanced_tablet_miniplayer_title">Enable tablet mini player</string>
|
<string name="revanced_miniplayer_screen_title">Miniplayer</string>
|
||||||
<string name="revanced_tablet_miniplayer_summary_on">Mini player is enabled</string>
|
<string name="revanced_miniplayer_screen_summary">Change the style of the in app minimized player</string>
|
||||||
<string name="revanced_tablet_miniplayer_summary_off">Mini player is disabled</string>
|
<string name="revanced_miniplayer_type_title">Miniplayer type</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_1">Original</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_2">Phone</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_3">Tablet</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_4">Modern 1</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_5">Modern 2</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_6">Modern 3</string>
|
||||||
|
<string name="revanced_miniplayer_hide_expand_close_title">Hide expand and close buttons</string>
|
||||||
|
<string name="revanced_miniplayer_hide_expand_close_summary_on">Buttons are hidden\n(swipe miniplayer to expand or close)</string>
|
||||||
|
<string name="revanced_miniplayer_hide_expand_close_summary_off">Expand and close buttons are shown</string>
|
||||||
|
<string name="revanced_miniplayer_hide_subtext_title">Hide subtexts</string>
|
||||||
|
<string name="revanced_miniplayer_hide_subtext_summary_on">Subtexts are hidden</string>
|
||||||
|
<string name="revanced_miniplayer_hide_subtext_summary_off">Subtexts are shown</string>
|
||||||
|
<string name="revanced_miniplayer_hide_rewind_forward_title">Hide skip forward and back buttons</string>
|
||||||
|
<string name="revanced_miniplayer_hide_rewind_forward_summary_on">Skip forward and back are hidden</string>
|
||||||
|
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Skip forward and back are shown</string>
|
||||||
|
<string name="revanced_miniplayer_opacity_title">Overlay opacity</string>
|
||||||
|
<string name="revanced_miniplayer_opacity_summary">Opacity value between 0-100, where 0 is transparent</string>
|
||||||
|
<string name="revanced_miniplayer_opacity_invalid_toast">Miniplayer overlay opacity must be between 0-100</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.theme.ThemeBytecodePatch">
|
<patch id="layout.theme.ThemeBytecodePatch">
|
||||||
<string name="revanced_gradient_loading_screen_title">Enable gradient loading screen</string>
|
<string name="revanced_gradient_loading_screen_title">Enable gradient loading screen</string>
|
||||||
@@ -1032,9 +1051,9 @@
|
|||||||
<string name="revanced_external_browser_summary_on">Opening links externally</string>
|
<string name="revanced_external_browser_summary_on">Opening links externally</string>
|
||||||
<string name="revanced_external_browser_summary_off">Opening links in app</string>
|
<string name="revanced_external_browser_summary_off">Opening links in app</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.minimizedplayback.MinimizedPlaybackPatch">
|
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||||
<string name="revanced_minimized_playback_title">Minimized playback</string>
|
<string name="revanced_background_playback_title">Background playback</string>
|
||||||
<string name="revanced_minimized_playback_summary">This setting can be found in Settings -> Background</string>
|
<string name="revanced_background_playback_summary">This setting can be found in Settings -> Background</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||||
<string name="revanced_remove_tracking_query_parameter_title">Remove tracking query parameter</string>
|
<string name="revanced_remove_tracking_query_parameter_title">Remove tracking query parameter</string>
|
||||||
@@ -1065,6 +1084,11 @@
|
|||||||
<string name="revanced_remember_video_quality_wifi">wifi</string>
|
<string name="revanced_remember_video_quality_wifi">wifi</string>
|
||||||
<string name="revanced_remember_video_quality_toast">Changed default %1$s quality to: %2$s</string>
|
<string name="revanced_remember_video_quality_toast">Changed default %1$s quality to: %2$s</string>
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
|
||||||
|
<string name="revanced_playback_speed_dialog_button_title">Show speed dialog button</string>
|
||||||
|
<string name="revanced_playback_speed_dialog_button_summary_on">Button is shown</string>
|
||||||
|
<string name="revanced_playback_speed_dialog_button_summary_off">Button is not shown</string>
|
||||||
|
</patch>
|
||||||
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
|
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
|
||||||
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
|
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
|
||||||
<string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string>
|
<string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string>
|
||||||
@@ -1096,7 +1120,7 @@
|
|||||||
<string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string>
|
<string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string>
|
||||||
<string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
|
<string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
|
||||||
<string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string>
|
<string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string>
|
||||||
<string name="revanced_spoof_client_use_ios_summary_on">Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Speed menu is missing\n• Watch history may not work\n• Live streams cannot play as audio only\n• Live streams not available on older devices</string>
|
<string name="revanced_spoof_client_use_ios_summary_on">Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Watch history may not work\n• Higher video qualities may be missing\n• Live streams cannot play as audio only\n• Live streams not available on Android 8.0</string>
|
||||||
<string name="revanced_spoof_client_use_ios_summary_off">Client is currently spoofed to Android VR\n\nSide effects include:\n• No HDR video\n• Kids videos do not playback\n• Paused videos can randomly resume</string>
|
<string name="revanced_spoof_client_use_ios_summary_off">Client is currently spoofed to Android VR\n\nSide effects include:\n• No HDR video\n• Kids videos do not playback\n• Paused videos can randomly resume</string>
|
||||||
<string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string>
|
<string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string>
|
||||||
<string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string>
|
<string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string>
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/slow_motion_video/materialsymbolsoutlined/slow_motion_video_wght200gradN25_24px.xml
|
||||||
|
The icon has been resized
|
||||||
|
|
||||||
|
|
||||||
|
Copyright 2022 Google
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M 6.0798492,16.70271 Q 5.3667836,15.77782 4.9972798,14.87317 4.6275665,13.96852 4.5,12.83942 h 0.7949215 q 0.1275665,0.9249 0.4677271,1.7656 0.3401706,0.8407 0.8797497,1.53346 z M 4.5,11.16058 Q 4.6373409,10.04469 5.002167,9.14154 5.3667836,8.2386 6.0798492,7.3137 L 6.6423983,7.87793 Q 6.1028192,8.5707 5.7626486,9.40308 5.422488,10.23568 5.2949215,11.16058 Z M 11.110466,19.5 Q 9.8992727,19.32286 9.0431213,18.95346 8.1871793,18.58406 7.2572507,17.89299 l 0.5627586,-0.57382 q 0.6818173,0.52458 1.5126748,0.89527 0.8308479,0.37047 1.7777819,0.49836 z M 7.8625281,6.66442 7.2899951,6.09059 Q 8.2197042,5.39953 9.0758557,5.03823 9.9320071,4.67714 11.152995,4.5 V 5.2872 Q 10.19861,5.41509 9.3677624,5.77746 8.5371243,6.13983 7.8625281,6.66442 Z M 10.32873,14.97101 V 9.02899 L 14.953488,12 Z M 12.827456,19.5 v -0.7872 q 2.53571,-0.36237 4.211668,-2.26097 1.675948,-1.89882 1.675948,-4.45183 0,-2.55301 -1.675948,-4.45183 Q 15.363166,5.64957 12.827456,5.2872 V 4.5 Q 15.70589,4.80844 17.60294,6.95069 19.5,9.09294 19.5,12 q 0,2.8964 -1.89706,5.04398 -1.89705,2.14758 -4.775484,2.45602 z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:yt="http://schemas.android.com/apk/res-auto" android:id="@+id/youtube_controls_bottom_ui_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutDirection="ltr">
|
||||||
|
<com.google.android.libraries.youtube.common.ui.TouchImageView android:id="@+id/revanced_playback_speed_dialog_button" android:paddingLeft="12dp" android:paddingTop="22dp" android:paddingRight="12dp" android:paddingBottom="16dp" android:longClickable="false" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/revanced_playback_speed_dialog_button" android:scaleType="center" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" style="@style/YouTubePlayerButton"/>
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user