mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-16 06:02:27 +01:00
Compare commits
28 Commits
v5.19.1-de
...
v5.21.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
476f13bf98 | ||
|
|
f216e16c0b | ||
|
|
f2a8789649 | ||
|
|
5973b64f52 | ||
|
|
102036706e | ||
|
|
2393d0a8f5 | ||
|
|
aea29b9522 | ||
|
|
4db8ef7079 | ||
|
|
7fbd26ccad | ||
|
|
91995ea01d | ||
|
|
86f867fe97 | ||
|
|
0f687ecfd3 | ||
|
|
6c8b7d09c1 | ||
|
|
3d6958f157 | ||
|
|
43d7cc7374 | ||
|
|
5ebd449f1f | ||
|
|
346a061df8 | ||
|
|
13e490a422 | ||
|
|
b4e8540bbc | ||
|
|
775c1baec2 | ||
|
|
9419fb8ec4 | ||
|
|
c510931eb0 | ||
|
|
7160699384 | ||
|
|
9db67a6eb2 | ||
|
|
e684d87dd3 | ||
|
|
2d1752a1eb | ||
|
|
c9ff7092fe | ||
|
|
d451bc6d6d |
102
CHANGELOG.md
102
CHANGELOG.md
@@ -1,3 +1,105 @@
|
||||
# [5.21.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0-dev.1) (2025-04-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
|
||||
|
||||
## [5.20.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
|
||||
|
||||
## [5.20.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1-dev.1) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
|
||||
|
||||
# [5.20.0](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
|
||||
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
|
||||
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
|
||||
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
|
||||
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
|
||||
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
|
||||
|
||||
# [5.20.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.6...v5.20.0-dev.7) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
|
||||
|
||||
# [5.20.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.5...v5.20.0-dev.6) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
|
||||
|
||||
# [5.20.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.4...v5.20.0-dev.5) (2025-04-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
|
||||
|
||||
# [5.20.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.3...v5.20.0-dev.4) (2025-04-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
|
||||
|
||||
# [5.20.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.2...v5.20.0-dev.3) (2025-04-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
|
||||
|
||||
# [5.20.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.1...v5.20.0-dev.2) (2025-04-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
|
||||
|
||||
# [5.20.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0-dev.1) (2025-04-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
|
||||
|
||||
## [5.19.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1) (2025-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
|
||||
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](https://github.com/ReVanced/revanced-patches/commit/89d44da171c3f56f13112d1d82bc4ea4a56c7c06))
|
||||
|
||||
## [5.19.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.1-dev.1...v5.19.1-dev.2) (2025-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
|
||||
|
||||
## [5.19.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1-dev.1) (2025-04-12)
|
||||
|
||||
|
||||
|
||||
@@ -319,6 +319,7 @@ public class Settings extends BaseSettings {
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.revanced.extension.youtube.swipecontrols
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import app.revanced.extension.shared.StringRef.str
|
||||
import app.revanced.extension.shared.Utils
|
||||
@@ -9,12 +8,8 @@ import app.revanced.extension.youtube.shared.PlayerType
|
||||
|
||||
/**
|
||||
* provider for configuration for volume and brightness swipe controls
|
||||
*
|
||||
* @param context the context to create in
|
||||
*/
|
||||
class SwipeControlsConfigurationProvider(
|
||||
private val context: Context,
|
||||
) {
|
||||
class SwipeControlsConfigurationProvider {
|
||||
//region swipe enable
|
||||
/**
|
||||
* should swipe controls be enabled? (global setting)
|
||||
@@ -60,6 +55,23 @@ class SwipeControlsConfigurationProvider(
|
||||
*/
|
||||
val swipeMagnitudeThreshold: Int
|
||||
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
|
||||
|
||||
/**
|
||||
* How much volume will change by single swipe.
|
||||
* If it is set to 0, it will reset to the default value because 0 would disable swiping.
|
||||
* */
|
||||
val volumeSwipeSensitivity: Int
|
||||
get() {
|
||||
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
|
||||
|
||||
if (sensitivity < 1) {
|
||||
Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
|
||||
|
||||
return Settings.SWIPE_VOLUME_SENSITIVITY.get()
|
||||
}
|
||||
|
||||
return sensitivity
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region overlay adjustments
|
||||
|
||||
@@ -127,7 +127,7 @@ class SwipeControlsHostActivity : Activity() {
|
||||
private fun initialize() {
|
||||
// create controllers
|
||||
printDebug { "initializing swipe controls controllers" }
|
||||
config = SwipeControlsConfigurationProvider(this)
|
||||
config = SwipeControlsConfigurationProvider()
|
||||
keys = VolumeKeysController(this)
|
||||
audio = createAudioController()
|
||||
screen = createScreenController()
|
||||
|
||||
@@ -41,7 +41,7 @@ class VolumeKeysController(
|
||||
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
|
||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||
controller.audio?.apply {
|
||||
volume += if (volumeUp) 1 else -1
|
||||
volume += controller.config.volumeSwipeSensitivity * if (volumeUp) 1 else -1
|
||||
controller.overlay.onVolumeChanged(volume, maxVolume)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ abstract class BaseGestureController(
|
||||
controller.overlay,
|
||||
10,
|
||||
1,
|
||||
controller.config.volumeSwipeSensitivity,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,6 +41,7 @@ interface VolumeAndBrightnessScroller {
|
||||
* @param overlayController overlay controller instance
|
||||
* @param volumeDistance unit distance for volume scrolling, in dp
|
||||
* @param brightnessDistance unit distance for brightness scrolling, in dp
|
||||
* @param volumeSwipeSensitivity how much volume will change by single swipe
|
||||
*/
|
||||
class VolumeAndBrightnessScrollerImpl(
|
||||
context: Context,
|
||||
@@ -49,6 +50,7 @@ class VolumeAndBrightnessScrollerImpl(
|
||||
private val overlayController: SwipeControlsOverlay,
|
||||
volumeDistance: Int = 10,
|
||||
brightnessDistance: Int = 1,
|
||||
private val volumeSwipeSensitivity: Int,
|
||||
) : VolumeAndBrightnessScroller {
|
||||
|
||||
// region volume
|
||||
@@ -60,7 +62,7 @@ class VolumeAndBrightnessScrollerImpl(
|
||||
),
|
||||
) { _, _, direction ->
|
||||
volumeController?.run {
|
||||
volume += direction
|
||||
volume += direction * volumeSwipeSensitivity
|
||||
overlayController.onVolumeChanged(volume, maxVolume)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class SwipeControlsOverlayLayout(
|
||||
private val config: SwipeControlsConfigurationProvider,
|
||||
) : RelativeLayout(context), SwipeControlsOverlay {
|
||||
|
||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider())
|
||||
|
||||
// Drawable icons for brightness and volume
|
||||
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.19.1-dev.1
|
||||
version = 5.21.0-dev.1
|
||||
|
||||
@@ -108,6 +108,10 @@ public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveSha
|
||||
public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
|
||||
public static final fun getSetTargetSdkVersion34 ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall {
|
||||
public abstract fun getDefinedClassName ()Ljava/lang/String;
|
||||
public abstract fun getMethodName ()Ljava/lang/String;
|
||||
@@ -838,6 +842,10 @@ public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt
|
||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatchKt {
|
||||
public static final fun getSpoofPackageInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
|
||||
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1408,6 +1416,8 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
|
||||
public static final fun is_20_07_or_greater ()Z
|
||||
public static final fun is_20_09_or_greater ()Z
|
||||
public static final fun is_20_10_or_greater ()Z
|
||||
public static final fun is_20_14_or_greater ()Z
|
||||
public static final fun is_20_15_or_greater ()Z
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
|
||||
@@ -1521,7 +1531,11 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
|
||||
}
|
||||
|
||||
public final class app/revanced/util/BytecodeUtilsKt {
|
||||
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
||||
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;I[I)I
|
||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
|
||||
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||
@@ -1548,9 +1562,17 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
||||
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
|
||||
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
|
||||
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
||||
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package app.revanced.patches.all.misc.targetSdk
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.util.getNode
|
||||
import org.w3c.dom.Element
|
||||
import java.util.logging.Logger
|
||||
|
||||
@Suppress("unused")
|
||||
val setTargetSdkVersion34 = resourcePatch(
|
||||
name = "Set target SDK version 34",
|
||||
description = "Changes the target SDK to version 34 (Android 14). " +
|
||||
"For devices running Android 15+, this will disable edge-to-edge display.",
|
||||
use = false,
|
||||
) {
|
||||
execute {
|
||||
val targetSdkOverride = 34 // Android 14.
|
||||
|
||||
document("AndroidManifest.xml").use { document ->
|
||||
fun getLogger() = Logger.getLogger(this::class.java.name)
|
||||
|
||||
// Ideally, the override should only be applied if the existing target is higher.
|
||||
// But since ApkTool does not add targetSdkVersion to the decompiled AndroidManifest,
|
||||
// there is no way to check targetSdkVersion. Instead, check compileSdkVersion and print a warning.
|
||||
try {
|
||||
val manifestElement = document.getNode("manifest") as Element
|
||||
val compileSdkVersion = Integer.parseInt(
|
||||
manifestElement.getAttribute("android:compileSdkVersion")
|
||||
)
|
||||
if (compileSdkVersion <= targetSdkOverride) {
|
||||
getLogger().warning(
|
||||
"This app does not appear to use a target SDK above $targetSdkOverride: " +
|
||||
"(compileSdkVersion: $compileSdkVersion)"
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
getLogger().warning("Could not check compileSdkVersion")
|
||||
}
|
||||
|
||||
// Change targetSdkVersion to override value.
|
||||
document.getElementsByTagName("manifest").item(0).let {
|
||||
var element = it.ownerDocument.createElement("uses-sdk")
|
||||
element.setAttribute("android:targetSdkVersion", targetSdkOverride.toString())
|
||||
|
||||
it.appendChild(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,8 @@ internal val initializeMonetizationDebugSettingsFingerprint = fingerprint {
|
||||
"Z", // useDebugBilling
|
||||
"Z", // showManageSubscriptions
|
||||
"Z", // alwaysShowSuperAds
|
||||
"Lcom/duolingo/debug/FamilyQuestOverride;",
|
||||
// matches "Lcom/duolingo/debug/FamilyQuestOverride;" or "Lcom/duolingo/data/debug/monetization/FamilyQuestOverride;"
|
||||
"Lcom/duolingo/",
|
||||
)
|
||||
opcodes(Opcode.IPUT_BOOLEAN)
|
||||
}
|
||||
|
||||
@@ -27,4 +27,5 @@ private fun gmsCoreSupportResourcePatch(
|
||||
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||
addStringResources = false,
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ private fun gmsCoreSupportResourcePatch(
|
||||
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
||||
fromPackageName = PHOTOS_PACKAGE_NAME,
|
||||
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
||||
addStringResources = false,
|
||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||
)
|
||||
|
||||
@@ -500,13 +500,44 @@ private object Constants {
|
||||
* @param executeBlock The additional execution block of the patch.
|
||||
* @param block The additional block to build the patch.
|
||||
*/
|
||||
fun gmsCoreSupportResourcePatch(
|
||||
fun gmsCoreSupportResourcePatch( // This is here only for binary compatibility.
|
||||
fromPackageName: String,
|
||||
toPackageName: String,
|
||||
spoofedPackageSignature: String,
|
||||
gmsCoreVendorGroupIdOption: Option<String>,
|
||||
executeBlock: ResourcePatchContext.() -> Unit = {},
|
||||
block: ResourcePatchBuilder.() -> Unit = {},
|
||||
) = gmsCoreSupportResourcePatch(
|
||||
fromPackageName,
|
||||
toPackageName,
|
||||
spoofedPackageSignature,
|
||||
gmsCoreVendorGroupIdOption,
|
||||
true,
|
||||
executeBlock,
|
||||
block
|
||||
)
|
||||
|
||||
/**
|
||||
* Abstract resource patch that allows Google apps to run without root and under a different package name
|
||||
* by using GmsCore instead of Google Play Services.
|
||||
*
|
||||
* @param fromPackageName The package name of the original app.
|
||||
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
|
||||
* @param spoofedPackageSignature The signature of the package to spoof to.
|
||||
* @param gmsCoreVendorGroupIdOption The option to get the vendor group ID of GmsCore.
|
||||
* @param addStringResources If the GmsCore shared strings should be added to the patched app.
|
||||
* @param executeBlock The additional execution block of the patch.
|
||||
* @param block The additional block to build the patch.
|
||||
*/
|
||||
// TODO: On the next major release make this public and delete the public overloaded constructor.
|
||||
internal fun gmsCoreSupportResourcePatch(
|
||||
fromPackageName: String,
|
||||
toPackageName: String,
|
||||
spoofedPackageSignature: String,
|
||||
gmsCoreVendorGroupIdOption: Option<String>,
|
||||
addStringResources: Boolean = true,
|
||||
executeBlock: ResourcePatchContext.() -> Unit = {},
|
||||
block: ResourcePatchBuilder.() -> Unit = {},
|
||||
) = resourcePatch {
|
||||
dependsOn(
|
||||
changePackageNamePatch,
|
||||
@@ -516,7 +547,10 @@ fun gmsCoreSupportResourcePatch(
|
||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
|
||||
execute {
|
||||
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
||||
// Some patches don't use shared String resources so there's no need to add them.
|
||||
if (addStringResources) {
|
||||
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
||||
}
|
||||
|
||||
/**
|
||||
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
||||
|
||||
@@ -14,7 +14,7 @@ import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
import app.revanced.util.returnEarly
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
@@ -235,7 +235,7 @@ fun spoofVideoStreamsPatch(
|
||||
|
||||
// region Fix iOS livestream current time.
|
||||
|
||||
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
hlsCurrentTimeFingerprint.method.insertLiteralOverride(
|
||||
HLS_CURRENT_TIME_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
|
||||
)
|
||||
@@ -245,21 +245,21 @@ fun spoofVideoStreamsPatch(
|
||||
// region turn off stream config replacement feature flag.
|
||||
|
||||
if (fixMediaFetchHotConfigChanges()) {
|
||||
mediaFetchHotConfigFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
mediaFetchHotConfigFingerprint.method.insertLiteralOverride(
|
||||
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
||||
)
|
||||
}
|
||||
|
||||
if (fixMediaFetchHotConfigAlternativeChanges()) {
|
||||
mediaFetchHotConfigAlternativeFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
mediaFetchHotConfigAlternativeFingerprint.method.insertLiteralOverride(
|
||||
MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
||||
)
|
||||
}
|
||||
|
||||
if (fixParsePlaybackResponseFeatureFlag()) {
|
||||
playbackStartDescriptorFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
playbackStartDescriptorFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||
PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z"
|
||||
)
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.*
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
|
||||
|
||||
internal val customThemeByteCodePatch = bytecodePatch {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
val backgroundColor by spotifyBackgroundColor
|
||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary
|
||||
|
||||
execute {
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
// Bytecode changes are not needed for legacy app target.
|
||||
// Player background color is changed with existing resource patch.
|
||||
return@execute
|
||||
}
|
||||
|
||||
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
|
||||
val index = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
const-string v$register, "$colorString"
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
|
||||
move-result-wide v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
val encoreColorsClassName = with(encoreThemeFingerprint) {
|
||||
// Find index of the first static get found after the string constant.
|
||||
val encoreColorsFieldReferenceIndex = originalMethod.indexOfFirstInstructionOrThrow(
|
||||
stringMatches!!.first().index,
|
||||
Opcode.SGET_OBJECT
|
||||
)
|
||||
|
||||
originalMethod.getInstruction(encoreColorsFieldReferenceIndex)
|
||||
.getReference<FieldReference>()!!.definingClass
|
||||
}
|
||||
|
||||
val encoreColorsConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
custom { method, classDef ->
|
||||
classDef.type == encoreColorsClassName &&
|
||||
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
|
||||
}
|
||||
}
|
||||
|
||||
encoreColorsConstructorFingerprint.method.apply {
|
||||
// Playlist song list background color.
|
||||
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
|
||||
|
||||
// Share menu background color.
|
||||
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||
}
|
||||
|
||||
homeCategoryPillColorsFingerprint.method.apply {
|
||||
// Home category pills background color.
|
||||
addColorChangeInstructions(HOME_CATEGORY_PILL_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||
}
|
||||
|
||||
settingsHeaderColorFingerprint.method.apply {
|
||||
// Settings header background color.
|
||||
addColorChangeInstructions(SETTINGS_HEADER_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,133 @@
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.booleanOption
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.*
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import org.w3c.dom.Element
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
|
||||
|
||||
internal val spotifyBackgroundColor = stringOption(
|
||||
key = "backgroundColor",
|
||||
default = "@android:color/black",
|
||||
title = "Primary background color",
|
||||
description = "The background color. Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val overridePlayerGradientColor = booleanOption(
|
||||
key = "overridePlayerGradientColor",
|
||||
default = false,
|
||||
title = "Override player gradient color",
|
||||
description = "Apply primary background color to the player gradient color, which changes dynamically with the song.",
|
||||
required = false
|
||||
)
|
||||
|
||||
internal val spotifyBackgroundColorSecondary = stringOption(
|
||||
key = "backgroundColorSecondary",
|
||||
default = "#FF121212",
|
||||
title = "Secondary background color",
|
||||
description =
|
||||
"The secondary background color. (e.g. playlist list in home, player artist, song credits). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyAccentColor = stringOption(
|
||||
key = "accentColor",
|
||||
default = "#FF1ED760",
|
||||
title = "Accent color",
|
||||
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyAccentColorPressed = stringOption(
|
||||
key = "accentColorPressed",
|
||||
default = "#FF169C46",
|
||||
title = "Pressed dark theme accent color",
|
||||
description =
|
||||
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
private val customThemeBytecodePatch = bytecodePatch {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
execute {
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
// Bytecode changes are not needed for legacy app target.
|
||||
// Player background color is changed with existing resource patch.
|
||||
return@execute
|
||||
}
|
||||
|
||||
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
|
||||
val index = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
const-string v$register, "$colorString"
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
|
||||
move-result-wide v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
val encoreColorsClassName = with(encoreThemeFingerprint.originalMethod) {
|
||||
// "Encore" colors are referenced right before the value of POSITIVE_INFINITY is returned.
|
||||
// Begin the instruction find using the index of where POSITIVE_INFINITY is set into the register.
|
||||
val positiveInfinityIndex = indexOfFirstLiteralInstructionOrThrow(
|
||||
Float.POSITIVE_INFINITY
|
||||
)
|
||||
val encoreColorsFieldReferenceIndex = indexOfFirstInstructionReversedOrThrow(
|
||||
positiveInfinityIndex,
|
||||
Opcode.SGET_OBJECT
|
||||
)
|
||||
|
||||
getInstruction(encoreColorsFieldReferenceIndex)
|
||||
.getReference<FieldReference>()!!.definingClass
|
||||
}
|
||||
|
||||
val encoreColorsConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
custom { method, classDef ->
|
||||
classDef.type == encoreColorsClassName &&
|
||||
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
|
||||
}
|
||||
}
|
||||
|
||||
val backgroundColor by spotifyBackgroundColor
|
||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary
|
||||
|
||||
encoreColorsConstructorFingerprint.method.apply {
|
||||
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
|
||||
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||
}
|
||||
|
||||
homeCategoryPillColorsFingerprint.method.addColorChangeInstructions(
|
||||
HOME_CATEGORY_PILL_COLOR_LITERAL,
|
||||
backgroundColorSecondary!!
|
||||
)
|
||||
|
||||
settingsHeaderColorFingerprint.method.addColorChangeInstructions(
|
||||
SETTINGS_HEADER_COLOR_LITERAL,
|
||||
backgroundColorSecondary!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
val customThemePatch = resourcePatch(
|
||||
name = "Custom theme",
|
||||
@@ -11,9 +136,10 @@ val customThemePatch = resourcePatch(
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
dependsOn(customThemeByteCodePatch)
|
||||
dependsOn(customThemeBytecodePatch)
|
||||
|
||||
val backgroundColor by spotifyBackgroundColor()
|
||||
val overridePlayerGradientColor by overridePlayerGradientColor()
|
||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary()
|
||||
val accentColor by spotifyAccentColor()
|
||||
val accentColorPressed by spotifyAccentColorPressed()
|
||||
@@ -25,31 +151,39 @@ val customThemePatch = resourcePatch(
|
||||
val childNodes = resourcesNode.childNodes
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i) as? Element ?: continue
|
||||
val name = node.getAttribute("name")
|
||||
|
||||
node.textContent = when (node.getAttribute("name")) {
|
||||
// Gradient next to user photo and "All" in home page
|
||||
// Skip overriding song/player gradient start color if the option is disabled.
|
||||
// Gradient end color should be themed regardless to allow the gradient to connect with
|
||||
// our primary background color.
|
||||
if (name == "bg_gradient_start_color" && !overridePlayerGradientColor!!) {
|
||||
continue
|
||||
}
|
||||
|
||||
node.textContent = when (name) {
|
||||
// Gradient next to user photo and "All" in home page.
|
||||
"dark_base_background_base",
|
||||
// Main background
|
||||
// Main background.
|
||||
"gray_7",
|
||||
// Left sidebar background in tablet mode
|
||||
// Left sidebar background in tablet mode.
|
||||
"gray_10",
|
||||
// Add account, Settings and privacy, View Profile left sidebar background
|
||||
// "Add account", "Settings and privacy", "View Profile" left sidebar background.
|
||||
"dark_base_background_elevated_base",
|
||||
// Song/player background
|
||||
// Song/player gradient start/end color.
|
||||
"bg_gradient_start_color", "bg_gradient_end_color",
|
||||
// Login screen
|
||||
"sthlm_blk", "sthlm_blk_grad_start", "stockholm_black",
|
||||
// Misc
|
||||
// Login screen background and gradient start.
|
||||
"sthlm_blk", "sthlm_blk_grad_start",
|
||||
// Misc.
|
||||
"image_placeholder_color",
|
||||
-> backgroundColor
|
||||
|
||||
// Track credits, merch in song player
|
||||
// Track credits, merch background in song player.
|
||||
"track_credits_card_bg", "benefit_list_default_color", "merch_card_background",
|
||||
// Playlist list background in home page
|
||||
// Playlist list background in home page.
|
||||
"opacity_white_10",
|
||||
// About artist background in song player
|
||||
// "About the artist" background in song player.
|
||||
"gray_15",
|
||||
// What's New pills background
|
||||
// "What's New" pills background.
|
||||
"dark_base_background_tinted_highlight"
|
||||
-> backgroundColorSecondary
|
||||
|
||||
@@ -59,5 +193,13 @@ val customThemePatch = resourcePatch(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Login screen gradient.
|
||||
document("res/drawable/start_screen_gradient.xml").use { document ->
|
||||
val gradientNode = document.getElementsByTagName("gradient").item(0) as Element
|
||||
|
||||
gradientNode.setAttribute("android:startColor", backgroundColor)
|
||||
gradientNode.setAttribute("android:endColor", backgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val encoreThemeFingerprint = fingerprint {
|
||||
strings("Encore theme was not provided.") // Partial string match.
|
||||
custom { method, _ ->
|
||||
method.name == "invoke"
|
||||
}
|
||||
}
|
||||
|
||||
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
|
||||
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
|
||||
internal const val PLAYLIST_BACKGROUND_COLOR_LITERAL = 0xFF121212
|
||||
internal const val SHARE_MENU_BACKGROUND_COLOR_LITERAL = 0xFF1F1F1F
|
||||
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
|
||||
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
|
||||
|
||||
internal val homeCategoryPillColorsFingerprint = fingerprint{
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
|
||||
internal val spotifyBackgroundColor = stringOption(
|
||||
key = "backgroundColor",
|
||||
default = "@android:color/black",
|
||||
title = "Primary background color",
|
||||
description = "The background color. Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyBackgroundColorSecondary = stringOption(
|
||||
key = "backgroundColorSecondary",
|
||||
default = "#FF121212",
|
||||
title = "Secondary background color",
|
||||
description = "The secondary background color. (e.g. playlist list, player arist, credits). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyAccentColor = stringOption(
|
||||
key = "accentColor",
|
||||
default = "#FF1ED760",
|
||||
title = "Accent color",
|
||||
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
internal val spotifyAccentColorPressed = stringOption(
|
||||
key = "accentColorPressed",
|
||||
default = "#FF169C46",
|
||||
title = "Pressed dark theme accent color",
|
||||
description =
|
||||
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
@@ -2,4 +2,8 @@ package app.revanced.patches.spotify.misc.fix
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal val getAppSignatureFingerprint = fingerprint { strings("Failed to get the application signatures") }
|
||||
internal val getPackageInfoFingerprint = fingerprint {
|
||||
strings(
|
||||
"Failed to get the application signatures"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package app.revanced.patches.spotify.misc.fix
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@Suppress("unused")
|
||||
val spoofPackageInfoPatch = bytecodePatch(
|
||||
name = "Spoof package info",
|
||||
description = "Spoofs the package info of the app to fix various functions of the app.",
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
execute {
|
||||
getPackageInfoFingerprint.method.apply {
|
||||
// region Spoof signature.
|
||||
|
||||
val failedToGetSignaturesStringIndex =
|
||||
getPackageInfoFingerprint.stringMatches!!.first().index
|
||||
|
||||
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
|
||||
failedToGetSignaturesStringIndex,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
)
|
||||
|
||||
val signatureRegister = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
|
||||
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
|
||||
|
||||
replaceInstruction(concatSignaturesIndex, "const-string v$signatureRegister, \"$expectedSignature\"")
|
||||
|
||||
// endregion
|
||||
|
||||
// region Spoof installer name.
|
||||
|
||||
val expectedInstallerName = "com.android.vending"
|
||||
|
||||
findInstructionIndicesReversedOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.name == "getInstallerPackageName" || reference?.name == "getInstallingPackageName"
|
||||
}.forEach { index ->
|
||||
val returnObjectIndex = index + 1
|
||||
|
||||
val installerPackageNameRegister = getInstruction<OneRegisterInstruction>(
|
||||
returnObjectIndex
|
||||
).registerA
|
||||
|
||||
addInstruction(
|
||||
returnObjectIndex + 1,
|
||||
"const-string v$installerPackageNameRegister, \"$expectedInstallerName\""
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,13 @@
|
||||
package app.revanced.patches.spotify.misc.fix
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Deprecated("Superseded by spoofPackageInfoPatch", ReplaceWith("spoofPackageInfoPatch"))
|
||||
@Suppress("unused")
|
||||
val spoofSignaturePatch = bytecodePatch(
|
||||
name = "Spoof signature",
|
||||
description = "Spoofs the signature of the app to fix various functions of the app.",
|
||||
description = "Spoofs the signature of the app fix various functions of the app.",
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
execute {
|
||||
getAppSignatureFingerprint.method.apply {
|
||||
val failedToGetSignaturesStringMatch = getAppSignatureFingerprint.stringMatches!!.first()
|
||||
|
||||
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
|
||||
failedToGetSignaturesStringMatch.index,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
)
|
||||
|
||||
val register = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
|
||||
|
||||
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
|
||||
|
||||
replaceInstruction(concatSignaturesIndex, "const-string v$register, \"$expectedSignature\"")
|
||||
}
|
||||
}
|
||||
dependsOn(spoofPackageInfoPatch)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ val hideAdsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -31,7 +31,8 @@ val hideGetPremiumPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -29,7 +29,8 @@ val videoAdsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -59,7 +59,8 @@ val copyVideoUrlPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -30,7 +30,8 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
val extensionMethodDescriptor =
|
||||
|
||||
@@ -74,7 +74,8 @@ val downloadsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -10,10 +10,15 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;"
|
||||
|
||||
val enableSeekbarTappingPatch = bytecodePatch(
|
||||
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
|
||||
) {
|
||||
@@ -31,39 +36,37 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
// Find the required methods to tap the seekbar.
|
||||
val patternMatch = onTouchEventHandlerFingerprint.patternMatch!!
|
||||
val seekbarTappingMethods = onTouchEventHandlerFingerprint.let {
|
||||
fun getMethodReference(index: Int) = it.method.getInstruction<ReferenceInstruction>(index)
|
||||
.reference as MethodReference
|
||||
|
||||
fun getReference(index: Int) = onTouchEventHandlerFingerprint.method.getInstruction<ReferenceInstruction>(index)
|
||||
.reference as MethodReference
|
||||
|
||||
val seekbarTappingMethods = buildMap {
|
||||
put("N", getReference(patternMatch.startIndex))
|
||||
put("O", getReference(patternMatch.endIndex))
|
||||
listOf(
|
||||
getMethodReference(it.patternMatch!!.startIndex),
|
||||
getMethodReference(it.patternMatch!!.endIndex)
|
||||
)
|
||||
}
|
||||
|
||||
val insertIndex = seekbarTappingFingerprint.patternMatch!!.endIndex - 1
|
||||
|
||||
seekbarTappingFingerprint.method.apply {
|
||||
val thisInstanceRegister = getInstruction<Instruction35c>(insertIndex - 1).registerC
|
||||
val pointIndex = indexOfNewPointInstruction(this)
|
||||
val invokeIndex = indexOfFirstInstructionOrThrow(pointIndex, Opcode.INVOKE_VIRTUAL)
|
||||
val insertIndex = invokeIndex + 1
|
||||
|
||||
val freeRegister = 0
|
||||
val xAxisRegister = 2
|
||||
val thisInstanceRegister = getInstruction<FiveRegisterInstruction>(invokeIndex).registerC
|
||||
val xAxisRegister = this.getInstruction<FiveRegisterInstruction>(pointIndex).registerD
|
||||
val freeRegister = findFreeRegister(insertIndex, thisInstanceRegister, xAxisRegister)
|
||||
|
||||
val oMethod = seekbarTappingMethods["O"]!!
|
||||
val nMethod = seekbarTappingMethods["N"]!!
|
||||
|
||||
fun MethodReference.toInvokeInstructionString() =
|
||||
"invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $this"
|
||||
val oMethod = seekbarTappingMethods[0]
|
||||
val nMethod = seekbarTappingMethods[1]
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { }, Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;->seekbarTappingEnabled()Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :disabled
|
||||
${oMethod.toInvokeInstructionString()}
|
||||
${nMethod.toInvokeInstructionString()}
|
||||
""",
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->seekbarTappingEnabled()Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :disabled
|
||||
invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $oMethod
|
||||
invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $nMethod
|
||||
""",
|
||||
ExternalLabel("disabled", getInstruction(insertIndex)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.indexOfFirstInstructionReversed
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
internal val swipingUpGestureParentFingerprint = fingerprint {
|
||||
@@ -101,14 +105,17 @@ internal val seekbarTappingFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("Z")
|
||||
parameters("L")
|
||||
opcodes(
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
// Insert seekbar tapping instructions here.
|
||||
Opcode.RETURN,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
)
|
||||
literal { Integer.MAX_VALUE.toLong() }
|
||||
custom { method, _ ->
|
||||
method.name == "onTouchEvent"
|
||||
&& method.containsLiteralInstruction(Integer.MAX_VALUE.toLong())
|
||||
&& indexOfNewPointInstruction(method) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
internal fun indexOfNewPointInstruction(method: Method) = method.indexOfFirstInstructionReversed {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Landroid/graphics/Point;"
|
||||
&& reference.name == "<init>"
|
||||
}
|
||||
|
||||
internal val slideToSeekFingerprint = fingerprint {
|
||||
|
||||
@@ -26,6 +26,7 @@ val seekbarPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ private val swipeControlsResourcePatch = resourcePatch {
|
||||
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
||||
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
|
||||
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
|
||||
TextPreference("revanced_swipe_volume_sensitivity", inputType = InputType.NUMBER),
|
||||
)
|
||||
|
||||
copyResources(
|
||||
@@ -86,7 +87,8 @@ val swipeControlsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -117,7 +119,7 @@ val swipeControlsPatch = bytecodePatch(
|
||||
// region patch to enable/disable swipe to change video.
|
||||
|
||||
if (is_19_43_or_greater) {
|
||||
swipeChangeVideoFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
swipeChangeVideoFingerprint.method.insertLiteralOverride(
|
||||
SWIPE_CHANGE_VIDEO_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,8 @@ val autoCaptionsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -49,7 +49,8 @@ val customBrandingPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
val appName by stringOption(
|
||||
|
||||
@@ -47,6 +47,7 @@ val changeHeaderPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ val hideButtonsPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -18,7 +18,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
@@ -46,7 +46,8 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -119,17 +120,17 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
|
||||
// Force on/off translucent effect on status bar and navigation buttons.
|
||||
if (is_19_25_or_greater) {
|
||||
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
|
||||
)
|
||||
|
||||
translucentNavigationButtonsFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
translucentNavigationButtonsFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
|
||||
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
|
||||
@@ -60,7 +60,8 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -39,7 +39,8 @@ val changeFormFactorPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -65,7 +65,8 @@ val hideEndscreenCardsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -37,7 +37,8 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -35,7 +35,8 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -16,9 +16,22 @@ internal val hideShowMoreButtonFingerprint = fingerprint {
|
||||
}
|
||||
|
||||
/**
|
||||
* 20.07+
|
||||
* 20.12+
|
||||
*/
|
||||
internal val parseElementFromBufferFingerprint = fingerprint {
|
||||
parameters("L", "L", "[B", "L", "L")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
)
|
||||
strings("Failed to parse Element") // String is a partial match.
|
||||
}
|
||||
|
||||
/**
|
||||
* 20.07+
|
||||
*/
|
||||
internal val parseElementFromBufferLegacy2007Fingerprint = fingerprint {
|
||||
parameters("L", "L", "[B", "L", "L")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
@@ -29,7 +42,10 @@ internal val parseElementFromBufferFingerprint = fingerprint {
|
||||
strings("Failed to parse Element") // String is a partial match.
|
||||
}
|
||||
|
||||
internal val parseElementFromBufferLegacyFingerprint = fingerprint {
|
||||
/**
|
||||
* 19.01 - 20.06
|
||||
*/
|
||||
internal val parseElementFromBufferLegacy1901Fingerprint = fingerprint {
|
||||
parameters("L", "L", "[B", "L", "L")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
|
||||
@@ -21,6 +21,7 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
@@ -132,7 +133,8 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -247,8 +249,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
|
||||
// region Mix playlists
|
||||
|
||||
(if (is_20_07_or_greater) parseElementFromBufferFingerprint
|
||||
else parseElementFromBufferLegacyFingerprint).let {
|
||||
(if (is_20_09_or_greater) parseElementFromBufferFingerprint
|
||||
else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint
|
||||
else parseElementFromBufferLegacy1901Fingerprint).let {
|
||||
it.method.apply {
|
||||
val byteArrayParameter = "p3"
|
||||
val startIndex = it.patternMatch!!.startIndex
|
||||
|
||||
@@ -63,7 +63,8 @@ val hideInfoCardsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -30,7 +30,8 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -35,7 +35,8 @@ val disableRollingNumberAnimationPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -178,7 +178,8 @@ val hideShortsComponentsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
hideShortsAppShortcutOption()
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
package app.revanced.patches.youtube.layout.hide.time
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal val timeCounterFingerprint = fingerprint(
|
||||
fuzzyPatternScanThreshold = 1,
|
||||
) {
|
||||
returns("V")
|
||||
internal val timeCounterFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters()
|
||||
opcodes(
|
||||
Opcode.SUB_LONG_2ADDR,
|
||||
Opcode.IGET_WIDE,
|
||||
Opcode.SUB_LONG_2ADDR,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_WIDE,
|
||||
Opcode.IGET_WIDE,
|
||||
Opcode.SUB_LONG_2ADDR
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ val hideTimestampPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstructio
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
@@ -172,7 +173,8 @@ val miniplayerPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -278,7 +280,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||
literal: Long,
|
||||
extensionMethod: String,
|
||||
) = method.insertFeatureFlagBooleanOverride(
|
||||
) = method.insertLiteralOverride(
|
||||
literal,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z"
|
||||
)
|
||||
@@ -346,7 +348,12 @@ val miniplayerPatch = bytecodePatch(
|
||||
// endregion
|
||||
|
||||
// region Legacy tablet miniplayer hooks.
|
||||
val appNameStringIndex = miniplayerOverrideFingerprint.stringMatches!!.first().index + 2
|
||||
val appNameStringIndex = miniplayerOverrideFingerprint.let {
|
||||
it.method.indexOfFirstInstructionOrThrow(it.stringMatches!!.first().index) {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.parameterTypes?.firstOrNull() == "Landroid/content/Context;"
|
||||
}
|
||||
}
|
||||
navigate(miniplayerOverrideFingerprint.originalMethod).to(appNameStringIndex).stop().apply {
|
||||
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ val playerPopupPanelsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -18,7 +18,8 @@ val playerControlsBackgroundPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -27,6 +27,7 @@ internal val exitFullscreenPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlaye
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
|
||||
@@ -24,7 +24,7 @@ internal val openVideosFullscreenHookPatch = bytecodePatch {
|
||||
return@execute
|
||||
}
|
||||
|
||||
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
openVideosFullscreenPortraitFingerprint.method.insertLiteralOverride(
|
||||
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@ val openVideosFullscreenPatch = bytecodePatch(
|
||||
"com.google.android.youtube"(
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ val customPlayerOverlayOpacityPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@@ -121,3 +122,12 @@ internal val textComponentLookupFingerprint = fingerprint {
|
||||
parameters("L")
|
||||
strings("…")
|
||||
}
|
||||
|
||||
internal const val LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG = 45675738L
|
||||
|
||||
internal val textComponentFeatureFlagFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.FINAL)
|
||||
returns("Z")
|
||||
parameters()
|
||||
literal { LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG }
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.addSettingPreference
|
||||
@@ -59,7 +60,8 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -174,6 +176,14 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
// Filter that parses the video id from the UI
|
||||
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
|
||||
|
||||
if (is_20_07_or_greater) {
|
||||
// Turn off a/b flag that enables new code for creating litho spans.
|
||||
// If enabled then the litho text span hook is never called.
|
||||
// Target code is very obfuscated and exactly what the code does is not clear.
|
||||
// Return late so debug patch logs if the flag is enabled.
|
||||
textComponentFeatureFlagFingerprint.method.returnLate(false)
|
||||
}
|
||||
|
||||
// Player response video id is needed to search for the video ids in Shorts litho components.
|
||||
hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ val wideSearchbarPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -28,9 +28,8 @@ import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
@@ -229,16 +228,9 @@ val seekbarColorPatch = bytecodePatch(
|
||||
|
||||
execute {
|
||||
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
|
||||
val index = indexOfFirstLiteralInstructionOrThrow(resourceId)
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(index, Opcode.MOVE_RESULT)
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I
|
||||
move-result v$register
|
||||
"""
|
||||
insertLiteralOverride(
|
||||
resourceId,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -354,7 +346,7 @@ val seekbarColorPatch = bytecodePatch(
|
||||
launchScreenLayoutTypeFingerprint,
|
||||
mainActivityOnCreateFingerprint
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
fingerprint.method.insertLiteralOverride(
|
||||
launchScreenLayoutTypeLotteFeatureFlag,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z"
|
||||
)
|
||||
|
||||
@@ -39,7 +39,8 @@ val shortsAutoplayPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -47,7 +47,8 @@ val openShortsInRegularPlayerPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -20,12 +20,6 @@ internal val appendTimeFingerprint = fingerprint {
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,8 @@ val sponsorBlockPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -64,7 +64,8 @@ val spoofAppVersionPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -35,7 +35,8 @@ val changeStartPagePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -40,7 +40,8 @@ val disableResumingShortsOnStartupPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -10,13 +10,6 @@ internal val lithoThemeFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters("Landroid/graphics/Rect;")
|
||||
opcodes(
|
||||
Opcode.APUT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IGET,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
|
||||
@@ -21,7 +21,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.forEachChildElement
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
import org.w3c.dom.Element
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
@@ -223,7 +223,8 @@ val themePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -233,7 +234,7 @@ val themePatch = bytecodePatch(
|
||||
SwitchPreference("revanced_gradient_loading_screen"),
|
||||
)
|
||||
|
||||
useGradientLoadingScreenFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
useGradientLoadingScreenFingerprint.method.insertLiteralOverride(
|
||||
GRADIENT_LOADING_SCREEN_AB_CONSTANT,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z"
|
||||
)
|
||||
|
||||
@@ -39,7 +39,8 @@ val alternativeThumbnailsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -33,7 +33,8 @@ val bypassImageRegionRestrictionsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -29,7 +29,8 @@ val announcementsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -30,7 +30,8 @@ val autoRepeatPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -12,6 +12,8 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||
@@ -44,6 +46,7 @@ val backgroundPlaybackPatch = bytecodePatch(
|
||||
playerTypeHookPatch,
|
||||
videoInformationPatch,
|
||||
settingsPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
@@ -54,7 +57,8 @@ val backgroundPlaybackPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
@@ -100,5 +104,13 @@ val backgroundPlaybackPatch = bytecodePatch(
|
||||
|
||||
// Force allowing background play for videos labeled for kids.
|
||||
kidsBackgroundPlaybackPolicyControllerFingerprint.method.returnEarly()
|
||||
|
||||
// Fix PiP buttons not working after locking/unlocking device screen.
|
||||
if (is_19_34_or_greater) {
|
||||
pipInputConsumerFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||
PIP_INPUT_CONSUMER_FEATURE_FLAG,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,4 +83,11 @@ internal val shortsBackgroundPlaybackFeatureFlagFingerprint = fingerprint {
|
||||
returns("Z")
|
||||
parameters()
|
||||
literal { 45415425 }
|
||||
}
|
||||
|
||||
internal const val PIP_INPUT_CONSUMER_FEATURE_FLAG = 45638483L
|
||||
|
||||
// Fix 'E/InputDispatcher: Window handle pip_input_consumer has no registered input channel'
|
||||
internal val pipInputConsumerFeatureFlagFingerprint = fingerprint {
|
||||
literal { PIP_INPUT_CONSUMER_FEATURE_FLAG}
|
||||
}
|
||||
@@ -40,7 +40,8 @@ val enableDebuggingPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -30,7 +30,8 @@ val spoofDeviceDimensionsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -27,7 +27,8 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -9,9 +9,7 @@ internal val onBackPressedFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
opcodes(Opcode.RETURN_VOID)
|
||||
custom { method, classDef ->
|
||||
method.name == "onBackPressed" &&
|
||||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|
||||
(classDef.endsWith("MainActivity;") || classDef.endsWith("WatchWhileActivity;"))
|
||||
method.name == "onBackPressed" && classDef.endsWith("MainActivity;")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +25,9 @@ internal val scrollPositionFingerprint = fingerprint {
|
||||
strings("scroll_position")
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves using class found in [recyclerViewTopScrollingParentFingerprint].
|
||||
*/
|
||||
internal val recyclerViewTopScrollingFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
|
||||
@@ -41,7 +41,8 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ val bypassURLRedirectsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -47,7 +47,8 @@ val openLinksExternallyPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -47,11 +47,7 @@ internal val mainActivityOnBackPressedFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters()
|
||||
custom { method, classDef ->
|
||||
val matchesClass = classDef.endsWith("MainActivity;") ||
|
||||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|
||||
classDef.endsWith("WatchWhileActivity;")
|
||||
|
||||
matchesClass && method.name == "onBackPressed"
|
||||
method.name == "onBackPressed" && classDef.endsWith("MainActivity;")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@ var is_20_09_or_greater = false
|
||||
private set
|
||||
var is_20_10_or_greater = false
|
||||
private set
|
||||
var is_20_14_or_greater = false
|
||||
private set
|
||||
var is_20_15_or_greater = false
|
||||
private set
|
||||
|
||||
val versionCheckPatch = resourcePatch(
|
||||
description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.",
|
||||
@@ -98,5 +102,7 @@ val versionCheckPatch = resourcePatch(
|
||||
is_20_07_or_greater = 250805000 <= playStoreServicesVersion
|
||||
is_20_09_or_greater = 251006000 <= playStoreServicesVersion
|
||||
is_20_10_or_greater = 251105000 <= playStoreServicesVersion
|
||||
is_20_14_or_greater = 251505000 <= playStoreServicesVersion
|
||||
is_20_15_or_greater = 251605000 <= playStoreServicesVersion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ val removeTrackingQueryParameterPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -269,7 +269,7 @@ val settingsPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// Add setting to force cairo settings fragment on/off.
|
||||
cairoFragmentConfigFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
cairoFragmentConfigFingerprint.method.insertLiteralOverride(
|
||||
CAIRO_CONFIG_LITERAL_VALUE,
|
||||
"$activityHookClassDescriptor->useCairoSettingsFragment(Z)Z"
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_03_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
@@ -22,7 +23,8 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
dependsOn(
|
||||
@@ -33,7 +35,8 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
|
||||
}, {
|
||||
is_19_34_or_greater
|
||||
}, {
|
||||
is_20_10_or_greater
|
||||
// In 20.14 the flag was merged with 20.03 start playback flag.
|
||||
is_20_10_or_greater && !is_20_14_or_greater
|
||||
}, {
|
||||
is_20_03_or_greater
|
||||
}, {
|
||||
|
||||
@@ -27,7 +27,8 @@ val zoomHapticsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -33,8 +33,7 @@ internal val mainActivityFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters()
|
||||
custom { _, classDef ->
|
||||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|
||||
classDef.endsWith("MainActivity;") || classDef.endsWith("WatchWhileActivity;")
|
||||
classDef.endsWith("MainActivity;")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +41,7 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters("Landroid/os/Bundle;")
|
||||
custom { method, classDef ->
|
||||
method.name == "onCreate" &&
|
||||
(
|
||||
classDef.endsWith("MainActivity;") ||
|
||||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|
||||
classDef.endsWith("WatchWhileActivity;")
|
||||
)
|
||||
method.name == "onCreate" && classDef.endsWith("MainActivity;")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ val forceOriginalAudioPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -34,7 +34,8 @@ val disableHdrPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
),
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
@@ -117,13 +117,6 @@ internal val playbackSpeedMenuSpeedChangedFingerprint = fingerprint {
|
||||
returns("L")
|
||||
parameters("L")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.SGET_OBJECT,
|
||||
|
||||
@@ -5,9 +5,35 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import org.stringtemplate.v4.compiler.Bytecode.instructions
|
||||
|
||||
/**
|
||||
* For targets 20.10 and later.
|
||||
* For targets 20.15 and later.
|
||||
*/
|
||||
internal val playerParameterBuilderFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("L")
|
||||
parameters(
|
||||
"Ljava/lang/String;", // VideoId.
|
||||
"[B",
|
||||
"Ljava/lang/String;", // Player parameters proto buffer.
|
||||
"Ljava/lang/String;",
|
||||
"I",
|
||||
"Z",
|
||||
"I",
|
||||
"L",
|
||||
"Ljava/util/Set;",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;",
|
||||
"L",
|
||||
"Z", // Appears to indicate if the video id is being opened or is currently playing.
|
||||
"Z",
|
||||
"Z"
|
||||
)
|
||||
strings("psps")
|
||||
}
|
||||
|
||||
/**
|
||||
* For targets 20.10 to 20.14.
|
||||
*/
|
||||
internal val playerParameterBuilder2010Fingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("L")
|
||||
parameters(
|
||||
|
||||
@@ -9,6 +9,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_23_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_02_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_15_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
|
||||
private val hooks = mutableSetOf<Hook>()
|
||||
@@ -39,9 +40,12 @@ val playerResponseMethodHookPatch = bytecodePatch {
|
||||
|
||||
execute {
|
||||
val fingerprint : Fingerprint
|
||||
if (is_20_10_or_greater) {
|
||||
if (is_20_15_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 13
|
||||
fingerprint = playerParameterBuilderFingerprint
|
||||
} else if (is_20_10_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 13
|
||||
fingerprint = playerParameterBuilder2010Fingerprint
|
||||
} else if (is_20_02_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 12
|
||||
fingerprint = playerParameterBuilder2002Fingerprint
|
||||
|
||||
@@ -29,6 +29,7 @@ val videoQualityPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ val playbackSpeedPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.07.39",
|
||||
"20.12.46",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -121,11 +121,11 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
|
||||
|
||||
// Override the min/max speeds that can be used.
|
||||
speedLimiterFingerprint.method.apply {
|
||||
val limitMinIndex = indexOfFirstLiteralInstructionOrThrow(0.25f.toRawBits().toLong())
|
||||
var limitMaxIndex = indexOfFirstLiteralInstruction(2.0f.toRawBits().toLong())
|
||||
val limitMinIndex = indexOfFirstLiteralInstructionOrThrow(0.25f)
|
||||
var limitMaxIndex = indexOfFirstLiteralInstruction(2.0f)
|
||||
// Newer targets have 4x max speed.
|
||||
if (limitMaxIndex < 0) {
|
||||
limitMaxIndex = indexOfFirstLiteralInstructionOrThrow(4.0f.toRawBits().toLong())
|
||||
limitMaxIndex = indexOfFirstLiteralInstructionOrThrow(4.0f)
|
||||
}
|
||||
|
||||
val limitMinRegister = getInstruction<OneRegisterInstruction>(limitMinIndex).registerA
|
||||
|
||||
@@ -14,6 +14,9 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.util.InstructionUtils.Companion.branchOpcodes
|
||||
import app.revanced.util.InstructionUtils.Companion.returnOpcodes
|
||||
import app.revanced.util.InstructionUtils.Companion.writeOpcodes
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.Opcode.*
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
@@ -43,7 +46,7 @@ import java.util.EnumSet
|
||||
* @throws IllegalArgumentException If a branch or conditional statement is encountered
|
||||
* before a suitable register is found.
|
||||
*/
|
||||
internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): Int {
|
||||
fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): Int {
|
||||
if (implementation == null) {
|
||||
throw IllegalArgumentException("Method has no implementation: $this")
|
||||
}
|
||||
@@ -51,82 +54,6 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude:
|
||||
throw IllegalArgumentException("startIndex out of bounds: $startIndex")
|
||||
}
|
||||
|
||||
// All registers used by an instruction.
|
||||
fun Instruction.getRegistersUsed() = when (this) {
|
||||
is FiveRegisterInstruction -> {
|
||||
when (registerCount) {
|
||||
1 -> listOf(registerC)
|
||||
2 -> listOf(registerC, registerD)
|
||||
3 -> listOf(registerC, registerD, registerE)
|
||||
4 -> listOf(registerC, registerD, registerE, registerF)
|
||||
else -> listOf(registerC, registerD, registerE, registerF, registerG)
|
||||
}
|
||||
}
|
||||
is ThreeRegisterInstruction -> listOf(registerA, registerB, registerC)
|
||||
is TwoRegisterInstruction -> listOf(registerA, registerB)
|
||||
is OneRegisterInstruction -> listOf(registerA)
|
||||
is RegisterRangeInstruction -> (startRegister until (startRegister + registerCount)).toList()
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
// Register that is written to by an instruction.
|
||||
fun Instruction.getWriteRegister() : Int {
|
||||
// Two and three register instructions extend OneRegisterInstruction.
|
||||
if (this is OneRegisterInstruction) return registerA
|
||||
throw IllegalStateException("Not a write instruction: $this")
|
||||
}
|
||||
|
||||
val writeOpcodes = EnumSet.of(
|
||||
ARRAY_LENGTH,
|
||||
INSTANCE_OF,
|
||||
NEW_INSTANCE, NEW_ARRAY,
|
||||
MOVE, MOVE_FROM16, MOVE_16, MOVE_WIDE, MOVE_WIDE_FROM16, MOVE_WIDE_16, MOVE_OBJECT,
|
||||
MOVE_OBJECT_FROM16, MOVE_OBJECT_16, MOVE_RESULT, MOVE_RESULT_WIDE, MOVE_RESULT_OBJECT, MOVE_EXCEPTION,
|
||||
CONST, CONST_4, CONST_16, CONST_HIGH16, CONST_WIDE_16, CONST_WIDE_32,
|
||||
CONST_WIDE, CONST_WIDE_HIGH16, CONST_STRING, CONST_STRING_JUMBO,
|
||||
IGET, IGET_WIDE, IGET_OBJECT, IGET_BOOLEAN, IGET_BYTE, IGET_CHAR, IGET_SHORT,
|
||||
IGET_VOLATILE, IGET_WIDE_VOLATILE, IGET_OBJECT_VOLATILE,
|
||||
SGET, SGET_WIDE, SGET_OBJECT, SGET_BOOLEAN, SGET_BYTE, SGET_CHAR, SGET_SHORT,
|
||||
SGET_VOLATILE, SGET_WIDE_VOLATILE, SGET_OBJECT_VOLATILE,
|
||||
AGET, AGET_WIDE, AGET_OBJECT, AGET_BOOLEAN, AGET_BYTE, AGET_CHAR, AGET_SHORT,
|
||||
// Arithmetic and logical operations.
|
||||
ADD_DOUBLE_2ADDR, ADD_DOUBLE, ADD_FLOAT_2ADDR, ADD_FLOAT, ADD_INT_2ADDR,
|
||||
ADD_INT_LIT8, ADD_INT, ADD_LONG_2ADDR, ADD_LONG, ADD_INT_LIT16,
|
||||
AND_INT_2ADDR, AND_INT_LIT8, AND_INT_LIT16, AND_INT, AND_LONG_2ADDR, AND_LONG,
|
||||
DIV_DOUBLE_2ADDR, DIV_DOUBLE, DIV_FLOAT_2ADDR, DIV_FLOAT, DIV_INT_2ADDR,
|
||||
DIV_INT_LIT16, DIV_INT_LIT8, DIV_INT, DIV_LONG_2ADDR, DIV_LONG,
|
||||
DOUBLE_TO_FLOAT, DOUBLE_TO_INT, DOUBLE_TO_LONG,
|
||||
FLOAT_TO_DOUBLE, FLOAT_TO_INT, FLOAT_TO_LONG,
|
||||
INT_TO_BYTE, INT_TO_CHAR, INT_TO_DOUBLE, INT_TO_FLOAT, INT_TO_LONG, INT_TO_SHORT,
|
||||
LONG_TO_DOUBLE, LONG_TO_FLOAT, LONG_TO_INT,
|
||||
MUL_DOUBLE_2ADDR, MUL_DOUBLE, MUL_FLOAT_2ADDR, MUL_FLOAT, MUL_INT_2ADDR,
|
||||
MUL_INT_LIT16, MUL_INT_LIT8, MUL_INT, MUL_LONG_2ADDR, MUL_LONG,
|
||||
NEG_DOUBLE, NEG_FLOAT, NEG_INT, NEG_LONG,
|
||||
NOT_INT, NOT_LONG,
|
||||
OR_INT_2ADDR, OR_INT_LIT16, OR_INT_LIT8, OR_INT, OR_LONG_2ADDR, OR_LONG,
|
||||
REM_DOUBLE_2ADDR, REM_DOUBLE, REM_FLOAT_2ADDR, REM_FLOAT, REM_INT_2ADDR,
|
||||
REM_INT_LIT16, REM_INT_LIT8, REM_INT, REM_LONG_2ADDR, REM_LONG,
|
||||
RSUB_INT_LIT8, RSUB_INT,
|
||||
SHL_INT_2ADDR, SHL_INT_LIT8, SHL_INT, SHL_LONG_2ADDR, SHL_LONG,
|
||||
SHR_INT_2ADDR, SHR_INT_LIT8, SHR_INT, SHR_LONG_2ADDR, SHR_LONG,
|
||||
SUB_DOUBLE_2ADDR, SUB_DOUBLE, SUB_FLOAT_2ADDR, SUB_FLOAT, SUB_INT_2ADDR,
|
||||
SUB_INT, SUB_LONG_2ADDR, SUB_LONG,
|
||||
USHR_INT_2ADDR, USHR_INT_LIT8, USHR_INT, USHR_LONG_2ADDR, USHR_LONG,
|
||||
XOR_INT_2ADDR, XOR_INT_LIT16, XOR_INT_LIT8, XOR_INT, XOR_LONG_2ADDR, XOR_LONG,
|
||||
)
|
||||
|
||||
val branchOpcodes = EnumSet.of(
|
||||
GOTO, GOTO_16, GOTO_32,
|
||||
IF_EQ, IF_NE, IF_LT, IF_GE, IF_GT, IF_LE,
|
||||
IF_EQZ, IF_NEZ, IF_LTZ, IF_GEZ, IF_GTZ, IF_LEZ,
|
||||
PACKED_SWITCH_PAYLOAD, SPARSE_SWITCH_PAYLOAD
|
||||
)
|
||||
|
||||
val returnOpcodes = EnumSet.of(
|
||||
RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT, RETURN_VOID_NO_BARRIER,
|
||||
THROW
|
||||
)
|
||||
|
||||
// Highest 4-bit register available, exclusive. Ideally return a free register less than this.
|
||||
val maxRegister4Bits = 16
|
||||
var bestFreeRegisterFound: Int? = null
|
||||
@@ -134,10 +61,9 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude:
|
||||
|
||||
for (i in startIndex until instructions.count()) {
|
||||
val instruction = getInstruction(i)
|
||||
val instructionRegisters = instruction.getRegistersUsed()
|
||||
val instructionRegisters = instruction.registersUsed
|
||||
|
||||
if (instruction.opcode in returnOpcodes) {
|
||||
// Method returns.
|
||||
if (instruction.isReturnInstruction) {
|
||||
usedRegisters.addAll(instructionRegisters)
|
||||
|
||||
// Use lowest register that hasn't been encountered.
|
||||
@@ -157,7 +83,7 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude:
|
||||
"$startIndex excluding: $registersToExclude")
|
||||
}
|
||||
|
||||
if (instruction.opcode in branchOpcodes) {
|
||||
if (instruction.isBranchInstruction) {
|
||||
if (bestFreeRegisterFound != null) {
|
||||
return bestFreeRegisterFound
|
||||
}
|
||||
@@ -165,9 +91,9 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude:
|
||||
throw IllegalArgumentException("Encountered a branch statement before a free register could be found")
|
||||
}
|
||||
|
||||
if (instruction.opcode in writeOpcodes) {
|
||||
val writeRegister = instruction.getWriteRegister()
|
||||
|
||||
val writeRegister = instruction.writeRegister
|
||||
if (writeRegister != null) {
|
||||
if (writeRegister !in usedRegisters) {
|
||||
// Verify the register is only used for write and not also as a parameter.
|
||||
// If the instruction uses the write register once then it's not also a read register.
|
||||
@@ -194,6 +120,53 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude:
|
||||
throw IllegalArgumentException("Start index is outside the range of normal control flow: $startIndex")
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The registers used by this instruction.
|
||||
*/
|
||||
internal val Instruction.registersUsed: List<Int>
|
||||
get() = when (this) {
|
||||
is FiveRegisterInstruction -> {
|
||||
when (registerCount) {
|
||||
1 -> listOf(registerC)
|
||||
2 -> listOf(registerC, registerD)
|
||||
3 -> listOf(registerC, registerD, registerE)
|
||||
4 -> listOf(registerC, registerD, registerE, registerF)
|
||||
else -> listOf(registerC, registerD, registerE, registerF, registerG)
|
||||
}
|
||||
}
|
||||
is ThreeRegisterInstruction -> listOf(registerA, registerB, registerC)
|
||||
is TwoRegisterInstruction -> listOf(registerA, registerB)
|
||||
is OneRegisterInstruction -> listOf(registerA)
|
||||
is RegisterRangeInstruction -> (startRegister until (startRegister + registerCount)).toList()
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The register that is written to by this instruction,
|
||||
* or NULL if this is not a write opcode.
|
||||
*/
|
||||
internal val Instruction.writeRegister: Int?
|
||||
get() {
|
||||
if (this.opcode !in writeOpcodes) {
|
||||
return null
|
||||
}
|
||||
if (this !is OneRegisterInstruction) {
|
||||
throw IllegalStateException("Not a write instruction: $this")
|
||||
}
|
||||
return registerA
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If this instruction is an unconditional or conditional branch opcode.
|
||||
*/
|
||||
internal val Instruction.isBranchInstruction: Boolean
|
||||
get() = this.opcode in branchOpcodes
|
||||
|
||||
/**
|
||||
* @return If this instruction returns or throws.
|
||||
*/
|
||||
internal val Instruction.isReturnInstruction: Boolean
|
||||
get() = this.opcode in returnOpcodes
|
||||
|
||||
/**
|
||||
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
|
||||
@@ -247,7 +220,7 @@ fun MutableMethod.injectHideViewCall(
|
||||
* (patch code)
|
||||
* (original code)
|
||||
*/
|
||||
internal fun MutableMethod.addInstructionsAtControlFlowLabel(
|
||||
fun MutableMethod.addInstructionsAtControlFlowLabel(
|
||||
insertIndex: Int,
|
||||
instructions: String,
|
||||
) {
|
||||
@@ -298,7 +271,7 @@ fun Method.indexOfFirstResourceIdOrThrow(resourceName: String): Int {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first literal instruction with the given value.
|
||||
* Find the index of the first literal instruction with the given long value.
|
||||
*
|
||||
* @return the first literal instruction with the value, or -1 if not found.
|
||||
* @see indexOfFirstLiteralInstructionOrThrow
|
||||
@@ -310,14 +283,56 @@ fun Method.indexOfFirstLiteralInstruction(literal: Long) = implementation?.let {
|
||||
} ?: -1
|
||||
|
||||
/**
|
||||
* Find the index of the first literal instruction with the given value,
|
||||
* Find the index of the first literal instruction with the given long value,
|
||||
* or throw an exception if not found.
|
||||
*
|
||||
* @return the first literal instruction with the value, or throws [PatchException] if not found.
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionOrThrow(literal: Long): Int {
|
||||
val index = indexOfFirstLiteralInstruction(literal)
|
||||
if (index < 0) throw PatchException("Could not find literal value: $literal")
|
||||
if (index < 0) throw PatchException("Could not find long literal: $literal")
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first literal instruction with the given float value.
|
||||
*
|
||||
* @return the first literal instruction with the value, or -1 if not found.
|
||||
* @see indexOfFirstLiteralInstructionOrThrow
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstruction(literal: Float) =
|
||||
indexOfFirstLiteralInstruction(literal.toRawBits().toLong())
|
||||
|
||||
/**
|
||||
* Find the index of the first literal instruction with the given float value,
|
||||
* or throw an exception if not found.
|
||||
*
|
||||
* @return the first literal instruction with the value, or throws [PatchException] if not found.
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionOrThrow(literal: Float): Int {
|
||||
val index = indexOfFirstLiteralInstruction(literal)
|
||||
if (index < 0) throw PatchException("Could not find float literal: $literal")
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the first literal instruction with the given double value.
|
||||
*
|
||||
* @return the first literal instruction with the value, or -1 if not found.
|
||||
* @see indexOfFirstLiteralInstructionOrThrow
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstruction(literal: Double) =
|
||||
indexOfFirstLiteralInstruction(literal.toRawBits().toLong())
|
||||
|
||||
/**
|
||||
* Find the index of the first literal instruction with the given double value,
|
||||
* or throw an exception if not found.
|
||||
*
|
||||
* @return the first literal instruction with the value, or throws [PatchException] if not found.
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionOrThrow(literal: Double): Int {
|
||||
val index = indexOfFirstLiteralInstruction(literal)
|
||||
if (index < 0) throw PatchException("Could not find double literal: $literal")
|
||||
return index
|
||||
}
|
||||
|
||||
@@ -334,24 +349,80 @@ fun Method.indexOfFirstLiteralInstructionReversed(literal: Long) = implementatio
|
||||
} ?: -1
|
||||
|
||||
/**
|
||||
* Find the index of the last wide literal instruction with the given value,
|
||||
* Find the index of the last wide literal instruction with the given long value,
|
||||
* or throw an exception if not found.
|
||||
*
|
||||
* @return the last literal instruction with the value, or throws [PatchException] if not found.
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionReversedOrThrow(literal: Long): Int {
|
||||
val index = indexOfFirstLiteralInstructionReversed(literal)
|
||||
if (index < 0) throw PatchException("Could not find literal value: $literal")
|
||||
if (index < 0) throw PatchException("Could not find long literal: $literal")
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the method contains a literal with the given value.
|
||||
* Find the index of the last literal instruction with the given float value.
|
||||
*
|
||||
* @return the last literal instruction with the value, or -1 if not found.
|
||||
* @see indexOfFirstLiteralInstructionOrThrow
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionReversed(literal: Float) =
|
||||
indexOfFirstLiteralInstructionReversed(literal.toRawBits().toLong())
|
||||
|
||||
/**
|
||||
* Find the index of the last wide literal instruction with the given float value,
|
||||
* or throw an exception if not found.
|
||||
*
|
||||
* @return the last literal instruction with the value, or throws [PatchException] if not found.
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionReversedOrThrow(literal: Float): Int {
|
||||
val index = indexOfFirstLiteralInstructionReversed(literal)
|
||||
if (index < 0) throw PatchException("Could not find float literal: $literal")
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the last literal instruction with the given double value.
|
||||
*
|
||||
* @return the last literal instruction with the value, or -1 if not found.
|
||||
* @see indexOfFirstLiteralInstructionOrThrow
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionReversed(literal: Double) =
|
||||
indexOfFirstLiteralInstructionReversed(literal.toRawBits().toLong())
|
||||
|
||||
/**
|
||||
* Find the index of the last wide literal instruction with the given double value,
|
||||
* or throw an exception if not found.
|
||||
*
|
||||
* @return the last literal instruction with the value, or throws [PatchException] if not found.
|
||||
*/
|
||||
fun Method.indexOfFirstLiteralInstructionReversedOrThrow(literal: Double): Int {
|
||||
val index = indexOfFirstLiteralInstructionReversed(literal)
|
||||
if (index < 0) throw PatchException("Could not find double literal: $literal")
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the method contains a literal with the given long value.
|
||||
*
|
||||
* @return if the method contains a literal with the given value.
|
||||
*/
|
||||
fun Method.containsLiteralInstruction(literal: Long) = indexOfFirstLiteralInstruction(literal) >= 0
|
||||
|
||||
/**
|
||||
* Check if the method contains a literal with the given float value.
|
||||
*
|
||||
* @return if the method contains a literal with the given value.
|
||||
*/
|
||||
fun Method.containsLiteralInstruction(literal: Float) = indexOfFirstLiteralInstruction(literal) >= 0
|
||||
|
||||
/**
|
||||
* Check if the method contains a literal with the given double value.
|
||||
*
|
||||
* @return if the method contains a literal with the given value.
|
||||
*/
|
||||
fun Method.containsLiteralInstruction(literal: Double) = indexOfFirstLiteralInstruction(literal) >= 0
|
||||
|
||||
/**
|
||||
* Traverse the class hierarchy starting from the given root class.
|
||||
*
|
||||
@@ -565,7 +636,12 @@ fun Method.findInstructionIndicesReversedOrThrow(opcode: Opcode): List<Int> {
|
||||
return instructions
|
||||
}
|
||||
|
||||
internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, extensionsMethod: String) {
|
||||
/**
|
||||
* Overrides the first move result with an extension call.
|
||||
* Suitable for calls to extension code to override boolean and integer values.
|
||||
*/
|
||||
internal fun MutableMethod.insertLiteralOverride(literal: Long, extensionMethodDescriptor: String) {
|
||||
// TODO: make this work with objects and wide values.
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val index = indexOfFirstInstructionOrThrow(literalIndex, MOVE_RESULT)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
@@ -579,9 +655,24 @@ internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, exten
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
$operation, $extensionsMethod
|
||||
$operation, $extensionMethodDescriptor
|
||||
move-result v$register
|
||||
""",
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a literal value result with a constant value.
|
||||
*/
|
||||
internal fun MutableMethod.insertLiteralOverride(literal: Long, override: Boolean) {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val index = indexOfFirstInstructionOrThrow(literalIndex, MOVE_RESULT)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
val overrideValue = if (override) "0x1" else "0x0"
|
||||
|
||||
addInstruction(
|
||||
index + 1,
|
||||
"const v$register, $overrideValue"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -607,29 +698,54 @@ fun BytecodePatchContext.forEachLiteralValueInstruction(
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the method early.
|
||||
* Overrides the first instruction of a method with a constant return value.
|
||||
* None of the method code will ever execute.
|
||||
*/
|
||||
fun MutableMethod.returnEarly(bool: Boolean = false) {
|
||||
fun MutableMethod.returnEarly(overrideValue: Boolean = false) = overrideReturnValue(overrideValue, false)
|
||||
|
||||
/**
|
||||
* Overrides all return statements with a constant value.
|
||||
* All method code is executed the same as unpatched.
|
||||
*
|
||||
* @see returnEarly
|
||||
*/
|
||||
internal fun MutableMethod.returnLate(overrideValue: Boolean = false) = overrideReturnValue(overrideValue, true)
|
||||
|
||||
private fun MutableMethod.overrideReturnValue(bool: Boolean, returnLate: Boolean) {
|
||||
val const = if (bool) "0x1" else "0x0"
|
||||
|
||||
val stringInstructions = when (returnType.first()) {
|
||||
'L' ->
|
||||
val instructions = when (returnType.first()) {
|
||||
'L' -> {
|
||||
"""
|
||||
const/4 v0, $const
|
||||
return-object v0
|
||||
"""
|
||||
}
|
||||
|
||||
'V' -> "return-void"
|
||||
'I', 'Z' ->
|
||||
'V' -> {
|
||||
if (returnLate) throw IllegalArgumentException("Cannot return late for method of void type")
|
||||
"return-void"
|
||||
}
|
||||
|
||||
'I', 'Z' -> {
|
||||
"""
|
||||
const/4 v0, $const
|
||||
return v0
|
||||
"""
|
||||
}
|
||||
|
||||
else -> throw Exception("Return type is not supported: $this")
|
||||
}
|
||||
|
||||
addInstructions(0, stringInstructions)
|
||||
if (returnLate) {
|
||||
findInstructionIndicesReversed {
|
||||
opcode == RETURN || opcode == RETURN_OBJECT
|
||||
}.forEach { index ->
|
||||
addInstructionsAtControlFlowLabel(index, instructions)
|
||||
}
|
||||
} else {
|
||||
addInstructions(0, instructions)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,3 +759,58 @@ fun FingerprintBuilder.literal(literalSupplier: () -> Long) {
|
||||
method.containsLiteralInstruction(literalSupplier())
|
||||
}
|
||||
}
|
||||
|
||||
private class InstructionUtils {
|
||||
companion object {
|
||||
val branchOpcodes: EnumSet<Opcode> = EnumSet.of(
|
||||
GOTO, GOTO_16, GOTO_32,
|
||||
IF_EQ, IF_NE, IF_LT, IF_GE, IF_GT, IF_LE,
|
||||
IF_EQZ, IF_NEZ, IF_LTZ, IF_GEZ, IF_GTZ, IF_LEZ,
|
||||
PACKED_SWITCH_PAYLOAD, SPARSE_SWITCH_PAYLOAD
|
||||
)
|
||||
|
||||
val returnOpcodes: EnumSet<Opcode> = EnumSet.of(
|
||||
RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT, RETURN_VOID_NO_BARRIER,
|
||||
THROW
|
||||
)
|
||||
|
||||
val writeOpcodes: EnumSet<Opcode> = EnumSet.of(
|
||||
ARRAY_LENGTH,
|
||||
INSTANCE_OF,
|
||||
NEW_INSTANCE, NEW_ARRAY,
|
||||
MOVE, MOVE_FROM16, MOVE_16, MOVE_WIDE, MOVE_WIDE_FROM16, MOVE_WIDE_16, MOVE_OBJECT,
|
||||
MOVE_OBJECT_FROM16, MOVE_OBJECT_16, MOVE_RESULT, MOVE_RESULT_WIDE, MOVE_RESULT_OBJECT, MOVE_EXCEPTION,
|
||||
CONST, CONST_4, CONST_16, CONST_HIGH16, CONST_WIDE_16, CONST_WIDE_32,
|
||||
CONST_WIDE, CONST_WIDE_HIGH16, CONST_STRING, CONST_STRING_JUMBO,
|
||||
IGET, IGET_WIDE, IGET_OBJECT, IGET_BOOLEAN, IGET_BYTE, IGET_CHAR, IGET_SHORT,
|
||||
IGET_VOLATILE, IGET_WIDE_VOLATILE, IGET_OBJECT_VOLATILE,
|
||||
SGET, SGET_WIDE, SGET_OBJECT, SGET_BOOLEAN, SGET_BYTE, SGET_CHAR, SGET_SHORT,
|
||||
SGET_VOLATILE, SGET_WIDE_VOLATILE, SGET_OBJECT_VOLATILE,
|
||||
AGET, AGET_WIDE, AGET_OBJECT, AGET_BOOLEAN, AGET_BYTE, AGET_CHAR, AGET_SHORT,
|
||||
// Arithmetic and logical operations.
|
||||
ADD_DOUBLE_2ADDR, ADD_DOUBLE, ADD_FLOAT_2ADDR, ADD_FLOAT, ADD_INT_2ADDR,
|
||||
ADD_INT_LIT8, ADD_INT, ADD_LONG_2ADDR, ADD_LONG, ADD_INT_LIT16,
|
||||
AND_INT_2ADDR, AND_INT_LIT8, AND_INT_LIT16, AND_INT, AND_LONG_2ADDR, AND_LONG,
|
||||
DIV_DOUBLE_2ADDR, DIV_DOUBLE, DIV_FLOAT_2ADDR, DIV_FLOAT, DIV_INT_2ADDR,
|
||||
DIV_INT_LIT16, DIV_INT_LIT8, DIV_INT, DIV_LONG_2ADDR, DIV_LONG,
|
||||
DOUBLE_TO_FLOAT, DOUBLE_TO_INT, DOUBLE_TO_LONG,
|
||||
FLOAT_TO_DOUBLE, FLOAT_TO_INT, FLOAT_TO_LONG,
|
||||
INT_TO_BYTE, INT_TO_CHAR, INT_TO_DOUBLE, INT_TO_FLOAT, INT_TO_LONG, INT_TO_SHORT,
|
||||
LONG_TO_DOUBLE, LONG_TO_FLOAT, LONG_TO_INT,
|
||||
MUL_DOUBLE_2ADDR, MUL_DOUBLE, MUL_FLOAT_2ADDR, MUL_FLOAT, MUL_INT_2ADDR,
|
||||
MUL_INT_LIT16, MUL_INT_LIT8, MUL_INT, MUL_LONG_2ADDR, MUL_LONG,
|
||||
NEG_DOUBLE, NEG_FLOAT, NEG_INT, NEG_LONG,
|
||||
NOT_INT, NOT_LONG,
|
||||
OR_INT_2ADDR, OR_INT_LIT16, OR_INT_LIT8, OR_INT, OR_LONG_2ADDR, OR_LONG,
|
||||
REM_DOUBLE_2ADDR, REM_DOUBLE, REM_FLOAT_2ADDR, REM_FLOAT, REM_INT_2ADDR,
|
||||
REM_INT_LIT16, REM_INT_LIT8, REM_INT, REM_LONG_2ADDR, REM_LONG,
|
||||
RSUB_INT_LIT8, RSUB_INT,
|
||||
SHL_INT_2ADDR, SHL_INT_LIT8, SHL_INT, SHL_LONG_2ADDR, SHL_LONG,
|
||||
SHR_INT_2ADDR, SHR_INT_LIT8, SHR_INT, SHR_LONG_2ADDR, SHR_LONG,
|
||||
SUB_DOUBLE_2ADDR, SUB_DOUBLE, SUB_FLOAT_2ADDR, SUB_FLOAT, SUB_INT_2ADDR,
|
||||
SUB_INT, SUB_LONG_2ADDR, SUB_LONG,
|
||||
USHR_INT_2ADDR, USHR_INT_LIT8, USHR_INT, USHR_LONG_2ADDR, USHR_LONG,
|
||||
XOR_INT_2ADDR, XOR_INT_LIT16, XOR_INT_LIT8, XOR_INT, XOR_LONG_2ADDR, XOR_LONG,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user