mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 18:03:55 +01:00
Compare commits
26 Commits
v4.0.0-dev
...
v4.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11d6eca36e | ||
|
|
eac360ee7c | ||
|
|
9acf15f571 | ||
|
|
126fe48ce6 | ||
|
|
32d2037083 | ||
|
|
aca1dd6074 | ||
|
|
30c6749f61 | ||
|
|
08c30791ae | ||
|
|
002f11a854 | ||
|
|
861e9a399e | ||
|
|
795aee13e4 | ||
|
|
09f29e6119 | ||
|
|
784aa2f246 | ||
|
|
4ddd5a0b8f | ||
|
|
1482e7e1e8 | ||
|
|
806e94481c | ||
|
|
edcb6eac6d | ||
|
|
4dced95d54 | ||
|
|
62c9085096 | ||
|
|
50a9f09703 | ||
|
|
9633b4eef0 | ||
|
|
2a94ad681c | ||
|
|
9e79e9e72c | ||
|
|
429badef1a | ||
|
|
138b2f8960 | ||
|
|
60d70e5877 |
77
CHANGELOG.md
77
CHANGELOG.md
@@ -1,3 +1,80 @@
|
||||
# [4.0.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.11...v4.0.0-dev.12) (2024-01-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support version `19.03.35` ([#2640](https://github.com/ReVanced/revanced-patches/issues/2640)) ([ff08f58](https://github.com/ReVanced/revanced-patches/commit/ff08f58ac4ff4d66a8dce599caa1ce47f3366fc6))
|
||||
|
||||
# [4.0.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.10...v4.0.0-dev.11) (2024-01-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support version `19.02.34` ([#2627](https://github.com/ReVanced/revanced-patches/issues/2627)) ([94e08b7](https://github.com/ReVanced/revanced-patches/commit/94e08b74ced394abf9ae7d4fe6355bfe4d0be248))
|
||||
|
||||
# [4.0.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.9...v4.0.0-dev.10) (2024-01-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support versions `18.48.39`, `18.49.37` and `19.01.34` ([#2551](https://github.com/ReVanced/revanced-patches/issues/2551)) ([a938e73](https://github.com/ReVanced/revanced-patches/commit/a938e736fa2aed1792cfdce5656efa15d0791d71))
|
||||
|
||||
# [4.0.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.8...v4.0.0-dev.9) (2024-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Enable slide to seek:** Change patch default to excluded and add description disclaimer ([#2610](https://github.com/ReVanced/revanced-patches/issues/2610)) ([2fdc4c2](https://github.com/ReVanced/revanced-patches/commit/2fdc4c23b5f39153ad71071359274c39129d691f))
|
||||
|
||||
# [4.0.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.7...v4.0.0-dev.8) (2024-01-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Shorten setting titles to fit on screen ([#2579](https://github.com/ReVanced/revanced-patches/issues/2579)) ([b2a5dd3](https://github.com/ReVanced/revanced-patches/commit/b2a5dd3efc39ae8a42159858b9c00b5b2f8655a4))
|
||||
|
||||
# [4.0.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.6...v4.0.0-dev.7) (2024-01-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Use new integrations patch path ([51e2f3b](https://github.com/ReVanced/revanced-patches/commit/51e2f3b476b49460e2f3fc2b5f302a3a72d7963f))
|
||||
|
||||
# [4.0.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.5...v4.0.0-dev.6) (2024-01-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Tiktok - Playback speed:** Remember playback speed ([#2506](https://github.com/ReVanced/revanced-patches/issues/2506)) ([d2970e5](https://github.com/ReVanced/revanced-patches/commit/d2970e54fbbd7e4b1ae1d354ae2d5c4bbe9336b0))
|
||||
|
||||
# [4.0.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.4...v4.0.0-dev.5) (2024-01-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Change header:** Improve patch descriptions ([#2581](https://github.com/ReVanced/revanced-patches/issues/2581)) ([43a5677](https://github.com/ReVanced/revanced-patches/commit/43a5677397380f14a049ae95532fd5096b94c938))
|
||||
|
||||
# [4.0.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.3...v4.0.0-dev.4) (2024-01-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **MyFitnessPal:** Add `Hide ads` patch ([#2594](https://github.com/ReVanced/revanced-patches/issues/2594)) ([fd4b3c7](https://github.com/ReVanced/revanced-patches/commit/fd4b3c79a83f8de6256611629263d3e29e66f2c2))
|
||||
|
||||
# [4.0.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.2...v4.0.0-dev.3) (2024-01-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Change package name:** Mention caveat of the patch in the description ([427b81a](https://github.com/ReVanced/revanced-patches/commit/427b81a79a5a1de79f14d2261059fb098b22227f))
|
||||
|
||||
# [4.0.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.1...v4.0.0-dev.2) (2024-01-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Change header:** Change to ReVanced borderless logo header by default ([#2512](https://github.com/ReVanced/revanced-patches/issues/2512)) ([75f785d](https://github.com/ReVanced/revanced-patches/commit/75f785d1ef6026cbbdf7073c10aace1b28d93a30))
|
||||
|
||||
# [4.0.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v3.3.0-dev.2...v4.0.0-dev.1) (2024-01-01)
|
||||
|
||||
|
||||
|
||||
@@ -333,6 +333,20 @@ public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatch : app
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/myfitnesspal/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/HideAdsPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/myfitnesspal/ads/fingerprints/IsPremiumUseCaseImplFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
|
||||
public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/fingerprints/IsPremiumUseCaseImplFingerprint;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/myfitnesspal/ads/fingerprints/MainActivityNavigateToNativePremiumUpsellFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
|
||||
public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/fingerprints/MainActivityNavigateToNativePremiumUpsellFingerprint;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch : app/revanced/patcher/patch/ResourcePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch;
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
@@ -1128,6 +1142,12 @@ public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPa
|
||||
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch : app/revanced/patcher/patch/ResourcePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch;
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch : app/revanced/patcher/patch/ResourcePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch;
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
kotlin.code.style = official
|
||||
version = 4.0.0-dev.1
|
||||
version = 4.0.0-dev.12
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
revanced-patcher = "19.1.0"
|
||||
revanced-patcher = "19.2.0"
|
||||
smali = "3.0.3"
|
||||
guava = "33.0.0-jre"
|
||||
gson = "2.10.1"
|
||||
|
||||
5045
package-lock.json
generated
5045
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.8.0",
|
||||
"gradle-semantic-release-plugin": "^1.9.0",
|
||||
"semantic-release": "^22.0.12"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -10,7 +10,7 @@ import java.io.Closeable
|
||||
|
||||
@Patch(
|
||||
name = "Change package name",
|
||||
description = "Appends \".revanced\" to the package name by default.",
|
||||
description = "Appends \".revanced\" to the package name by default. Changing the package name of the app can lead to unexpected issues.",
|
||||
use = false
|
||||
)
|
||||
@Suppress("unused")
|
||||
@@ -59,4 +59,4 @@ object ChangePackageNamePatch : ResourcePatch(), Closeable {
|
||||
|
||||
manifest.setAttribute("package", replacementPackageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
||||
|
||||
abstract fun filterMap(
|
||||
classDef: ClassDef,
|
||||
method: Method,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package app.revanced.patches.myfitnesspal.ads
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.myfitnesspal.ads.fingerprints.IsPremiumUseCaseImplFingerprint
|
||||
import app.revanced.patches.myfitnesspal.ads.fingerprints.MainActivityNavigateToNativePremiumUpsellFingerprint
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Hide ads",
|
||||
description = "Hides most of the ads across the app.",
|
||||
compatiblePackages = [CompatiblePackage("com.myfitnesspal.android")]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object HideAdsPatch : BytecodePatch(
|
||||
setOf(IsPremiumUseCaseImplFingerprint, MainActivityNavigateToNativePremiumUpsellFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Overwrite the premium status specifically for ads.
|
||||
IsPremiumUseCaseImplFingerprint.result?.mutableMethod?.replaceInstructions(
|
||||
0,
|
||||
"""
|
||||
sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;
|
||||
return-object v0
|
||||
"""
|
||||
) ?: throw IsPremiumUseCaseImplFingerprint.exception
|
||||
|
||||
// Prevent the premium upsell dialog from showing when the main activity is launched.
|
||||
// In other places that are premium-only the dialog will still show.
|
||||
MainActivityNavigateToNativePremiumUpsellFingerprint.result?.mutableMethod?.replaceInstructions(
|
||||
0,
|
||||
"return-void"
|
||||
) ?: throw MainActivityNavigateToNativePremiumUpsellFingerprint.exception
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package app.revanced.patches.myfitnesspal.ads.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object IsPremiumUseCaseImplFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC.value,
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
classDef.type.endsWith("IsPremiumUseCaseImpl;") && methodDef.name == "doWork"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.myfitnesspal.ads.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object MainActivityNavigateToNativePremiumUpsellFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
classDef.type.endsWith("MainActivity;") && methodDef.name == "navigateToNativePremiumUpsell"
|
||||
}
|
||||
)
|
||||
@@ -1,49 +1,82 @@
|
||||
package app.revanced.patches.tiktok.interaction.speed
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SpeedControlParentFingerprint
|
||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.GetSpeedFingerprint
|
||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.OnRenderFirstFrameFingerprint
|
||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SetSpeedFingerprint
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@Patch(
|
||||
name = "Playback speed",
|
||||
description = "Enables the playback speed option for all videos.",
|
||||
description = "Enables the playback speed option for all videos and " +
|
||||
"retains the speed configurations in between videos.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
||||
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object PlaybackSpeedPatch : BytecodePatch(setOf(SpeedControlParentFingerprint)) {
|
||||
object PlaybackSpeedPatch : BytecodePatch(
|
||||
setOf(
|
||||
GetSpeedFingerprint,
|
||||
OnRenderFirstFrameFingerprint,
|
||||
SetSpeedFingerprint
|
||||
)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
SpeedControlParentFingerprint.result?.mutableMethod?.apply {
|
||||
val targetMethodCallIndex = indexOfFirstInstruction {
|
||||
if (opcode == Opcode.INVOKE_STATIC) {
|
||||
val paramsTypes = ((this as Instruction35c).reference as MethodReference).parameterTypes
|
||||
paramsTypes.size == 1 && paramsTypes[0].contains("/Aweme;")
|
||||
} else false
|
||||
}
|
||||
SetSpeedFingerprint.result?.let { onVideoSwiped ->
|
||||
// Remember the playback speed of the current video.
|
||||
GetSpeedFingerprint.result?.mutableMethod?.apply {
|
||||
val injectIndex = indexOfFirstInstruction { getReference<MethodReference>()?.returnType == "F" } + 2
|
||||
val register = getInstruction<Instruction11x>(injectIndex - 1).registerA
|
||||
|
||||
val isSpeedEnableMethod = context
|
||||
.toMethodWalker(this)
|
||||
.nextMethod(targetMethodCallIndex, true)
|
||||
.getMethod() as MutableMethod
|
||||
addInstruction(
|
||||
injectIndex,
|
||||
"invoke-static { v$register }," +
|
||||
" Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V"
|
||||
)
|
||||
} ?: throw GetSpeedFingerprint.exception
|
||||
|
||||
isSpeedEnableMethod.addInstructions(
|
||||
// By default, the playback speed will reset to 1.0 at the start of each video.
|
||||
// Instead, override it with the desired playback speed.
|
||||
OnRenderFirstFrameFingerprint.result?.mutableMethod?.addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
# Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method.
|
||||
const/4 v0, 0x1
|
||||
invoke-virtual {p0, v0}, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getEnterFrom(Z)Ljava/lang/String;
|
||||
move-result-object v0
|
||||
|
||||
# Model of current video retrieved using getCurrentAweme method.
|
||||
invoke-virtual {p0}, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme;
|
||||
move-result-object v1
|
||||
|
||||
# Desired playback speed retrieved using getPlaybackSpeed method.
|
||||
invoke-static {}, Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F
|
||||
move-result-object v2
|
||||
invoke-static { v0, v1, v2 }, ${onVideoSwiped.method}
|
||||
"""
|
||||
) ?: throw OnRenderFirstFrameFingerprint.exception
|
||||
|
||||
// Force enable the playback speed option for all videos.
|
||||
onVideoSwiped.mutableClass.methods.find { method -> method.returnType == "Z" }?.addInstructions(
|
||||
0,
|
||||
"""
|
||||
)
|
||||
} ?: throw SpeedControlParentFingerprint.exception
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
"""
|
||||
) ?: throw PatchException("Failed to force enable the playback speed option.")
|
||||
} ?: throw SetSpeedFingerprint.exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object GetSpeedFingerprint : MethodFingerprint(
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.definingClass.endsWith("/BaseListFragmentPanel;") && methodDef.name == "onFeedSpeedSelectedEvent"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object OnRenderFirstFrameFingerprint : MethodFingerprint(
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.definingClass.endsWith("/BaseListFragmentPanel;") && methodDef.name == "onRenderFirstFrame"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object SetSpeedFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
parameters = listOf(
|
||||
"Ljava/lang/String;",
|
||||
"Lcom/ss/android/ugc/aweme/feed/model/Aweme;",
|
||||
"F"
|
||||
),
|
||||
strings = listOf("enterFrom")
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object SpeedControlParentFingerprint : MethodFingerprint(
|
||||
strings = listOf(
|
||||
"onStopTrackingTouch, hasTouchMove=",
|
||||
", isCurVideoPaused: ",
|
||||
"already_shown_edge_speed_guide"
|
||||
)
|
||||
)
|
||||
@@ -24,15 +24,18 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -25,8 +25,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -29,8 +29,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -17,15 +17,12 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"com.google.android.youtube", [
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -21,7 +21,19 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube"
|
||||
"com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
@@ -17,15 +17,12 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"com.google.android.youtube", [
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
@@ -27,8 +27,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -29,8 +29,12 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
[
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -17,7 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Enable slide to seek",
|
||||
description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player.",
|
||||
description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player. Including this patch may cause issues with tapping or double tapping the video player overlay.",
|
||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
@@ -25,11 +25,16 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
[
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
],
|
||||
use = false
|
||||
)
|
||||
@Suppress("unused")
|
||||
object EnableSlideToSeekPatch : BytecodePatch(
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package app.revanced.patches.youtube.interaction.swipecontrols
|
||||
|
||||
import app.revanced.util.transformMethods
|
||||
import app.revanced.util.traverseClassHierarchy
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
@@ -10,7 +8,9 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
|
||||
import app.revanced.patches.youtube.interaction.swipecontrols.fingerprints.SwipeControlsHostActivityFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.WatchWhileActivityFingerprint
|
||||
import app.revanced.patches.youtube.shared.fingerprints.MainActivityFingerprint
|
||||
import app.revanced.util.transformMethods
|
||||
import app.revanced.util.traverseClassHierarchy
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
|
||||
@@ -31,8 +31,12 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -40,19 +44,19 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
@Suppress("unused")
|
||||
object SwipeControlsBytecodePatch : BytecodePatch(
|
||||
setOf(
|
||||
WatchWhileActivityFingerprint,
|
||||
MainActivityFingerprint,
|
||||
SwipeControlsHostActivityFingerprint
|
||||
)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val wrapperClass = SwipeControlsHostActivityFingerprint.result!!.mutableClass
|
||||
val targetClass = WatchWhileActivityFingerprint.result!!.mutableClass
|
||||
val targetClass = MainActivityFingerprint.result!!.mutableClass
|
||||
|
||||
// inject the wrapper class from integrations into the class hierarchy of WatchWhileActivity
|
||||
// Inject the wrapper class from integrations into the class hierarchy of MainActivity.
|
||||
wrapperClass.setSuperClass(targetClass.superclass)
|
||||
targetClass.setSuperClass(wrapperClass.type)
|
||||
|
||||
// ensure all classes and methods in the hierarchy are non-final, so we can override them in integrations
|
||||
// Ensure all classes and methods in the hierarchy are non-final, so we can override them in integrations.
|
||||
context.traverseClassHierarchy(targetClass) {
|
||||
accessFlags = accessFlags and AccessFlags.FINAL.value.inv()
|
||||
transformMethods {
|
||||
|
||||
@@ -29,8 +29,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
],
|
||||
|
||||
@@ -93,7 +93,7 @@ object CustomBrandingPatch : ResourcePatch() {
|
||||
)
|
||||
}
|
||||
}
|
||||
} else resourceGroups.forEach { context.copyResources("branding", it) }
|
||||
} else resourceGroups.forEach { context.copyResources("custom-branding", it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package app.revanced.patches.youtube.layout.branding.header
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
import java.io.File
|
||||
|
||||
@Patch(
|
||||
name = "Change header",
|
||||
description = "Applies a custom header in the top left corner within the app. Defaults to the ReVanced header.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage("com.google.android.youtube")
|
||||
],
|
||||
use = false
|
||||
)
|
||||
@Suppress("unused")
|
||||
object ChangeHeaderPatch : ResourcePatch() {
|
||||
private const val HEADER_NAME = "yt_wordmark_header"
|
||||
private const val PREMIUM_HEADER_NAME = "yt_premium_wordmark_header"
|
||||
private const val REVANCED_HEADER_NAME = "ReVanced"
|
||||
private const val REVANCED_BORDERLESS_HEADER_NAME = "ReVanced (borderless logo)"
|
||||
|
||||
private val targetResourceDirectoryNames = arrayOf(
|
||||
"xxxhdpi",
|
||||
"xxhdpi",
|
||||
"xhdpi",
|
||||
"mdpi",
|
||||
"hdpi",
|
||||
).map { dpi ->
|
||||
"drawable-$dpi"
|
||||
}
|
||||
|
||||
private val variants = arrayOf("light", "dark")
|
||||
|
||||
private val header by stringPatchOption(
|
||||
key = "header",
|
||||
default = REVANCED_BORDERLESS_HEADER_NAME,
|
||||
values = mapOf(
|
||||
"YouTube" to HEADER_NAME,
|
||||
"YouTube Premium" to PREMIUM_HEADER_NAME,
|
||||
"ReVanced" to REVANCED_HEADER_NAME,
|
||||
"ReVanced (borderless logo)" to REVANCED_BORDERLESS_HEADER_NAME,
|
||||
),
|
||||
title = "Header",
|
||||
description = """
|
||||
Either a header name or a path to a custom header folder to use in the top bar.
|
||||
The path to a folder must contain one or more of the following folders matching the DPI of your device:
|
||||
|
||||
${targetResourceDirectoryNames.joinToString("\n") { "- $it" }}
|
||||
|
||||
These folders must contain the following files:
|
||||
|
||||
${variants.joinToString("\n") { variant -> "- ${HEADER_NAME}_$variant.png" }}
|
||||
""".trimIndent(),
|
||||
required = true,
|
||||
)
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
// The directories to copy the header to.
|
||||
val targetResourceDirectories = targetResourceDirectoryNames.mapNotNull {
|
||||
context["res"].resolve(it).takeIf(File::exists)
|
||||
}
|
||||
// The files to replace in the target directories.
|
||||
val targetResourceFiles = targetResourceDirectoryNames.map { directoryName ->
|
||||
ResourceGroup(
|
||||
directoryName,
|
||||
*variants.map { variant -> "${HEADER_NAME}_$variant.png" }.toTypedArray()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that overwrites both header variants from [from] to [to] in the target resource directories.
|
||||
*/
|
||||
val overwriteFromTo: (String, String) -> Unit = { from: String, to: String ->
|
||||
targetResourceDirectories.forEach { directory ->
|
||||
variants.forEach { variant ->
|
||||
val fromPath = directory.resolve("${from}_$variant.png")
|
||||
val toPath = directory.resolve("${to}_$variant.png")
|
||||
|
||||
fromPath.copyTo(toPath, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Functions to overwrite the header to the different variants.
|
||||
val toPremium = { overwriteFromTo(PREMIUM_HEADER_NAME, HEADER_NAME) }
|
||||
val toHeader = { overwriteFromTo(HEADER_NAME, PREMIUM_HEADER_NAME) }
|
||||
val toReVanced = {
|
||||
// Copy the ReVanced header to the resource directories.
|
||||
targetResourceFiles.forEach { context.copyResources("change-header/revanced", it) }
|
||||
|
||||
// Overwrite the premium with the custom header as well.
|
||||
toHeader()
|
||||
}
|
||||
val toReVancedBorderless = {
|
||||
// Copy the ReVanced borderless header to the resource directories.
|
||||
targetResourceFiles.forEach { context.copyResources("change-header/revanced-borderless", it) }
|
||||
|
||||
// Overwrite the premium with the custom header as well.
|
||||
toHeader()
|
||||
}
|
||||
val toCustom = {
|
||||
var copiedReplacementImages = false
|
||||
// For all the resource groups in the custom header folder, copy them to the resource directories.
|
||||
File(header!!).listFiles { file -> file.isDirectory }?.forEach { folder ->
|
||||
val targetDirectory = context["res"].resolve(folder.name)
|
||||
// Skip if the target directory (DPI) doesn't exist.
|
||||
if (!targetDirectory.exists()) return@forEach
|
||||
|
||||
folder.listFiles { file -> file.isFile }?.forEach {
|
||||
val targetResourceFile = targetDirectory.resolve(it.name)
|
||||
|
||||
it.copyTo(targetResourceFile, true)
|
||||
copiedReplacementImages = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!copiedReplacementImages) throw PatchException("Could not find any custom images resources in directory: $header")
|
||||
|
||||
// Overwrite the premium with the custom header as well.
|
||||
toHeader()
|
||||
}
|
||||
|
||||
when (header) {
|
||||
HEADER_NAME -> toHeader
|
||||
PREMIUM_HEADER_NAME -> toPremium
|
||||
REVANCED_HEADER_NAME -> toReVanced
|
||||
REVANCED_BORDERLESS_HEADER_NAME -> toReVancedBorderless
|
||||
else -> toCustom
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -1,62 +1,9 @@
|
||||
package app.revanced.patches.youtube.layout.branding.header
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
||||
import kotlin.io.path.copyTo
|
||||
|
||||
@Patch(
|
||||
name = "Premium heading",
|
||||
description = "Adds or removes the YouTube Premium logo at the top of feeds.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage("com.google.android.youtube")
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
@Deprecated("Use PremiumHeadingPatch instead.")
|
||||
object PremiumHeadingPatch : ResourcePatch() {
|
||||
private const val DEFAULT_HEADING_RES = "yt_wordmark_header"
|
||||
private const val PREMIUM_HEADING_RES = "yt_premium_wordmark_header"
|
||||
|
||||
private val usePremiumHeading by booleanPatchOption(
|
||||
key = "usePremiumHeading",
|
||||
default = true,
|
||||
title = "Use premium heading",
|
||||
description = "Whether to use the YouTube Premium logo.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
val resDirectory = context["res"]
|
||||
|
||||
val (original, replacement) = if (usePremiumHeading!!)
|
||||
PREMIUM_HEADING_RES to DEFAULT_HEADING_RES
|
||||
else
|
||||
DEFAULT_HEADING_RES to PREMIUM_HEADING_RES
|
||||
|
||||
val variants = arrayOf("light", "dark")
|
||||
|
||||
arrayOf(
|
||||
"xxxhdpi",
|
||||
"xxhdpi",
|
||||
"xhdpi",
|
||||
"hdpi",
|
||||
"mdpi"
|
||||
).mapNotNull { dpi ->
|
||||
resDirectory.resolve("drawable-$dpi").takeIf { it.exists() }?.toPath()
|
||||
}.also {
|
||||
if (it.isEmpty())
|
||||
throw PatchException("The drawable folder can not be found. Therefore, the patch can not be applied.")
|
||||
}.forEach { path ->
|
||||
|
||||
variants.forEach { mode ->
|
||||
val fromPath = path.resolve("${original}_$mode.png")
|
||||
val toPath = path.resolve("${replacement}_$mode.png")
|
||||
|
||||
fromPath.copyTo(toPath, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun execute(context: ResourceContext) = ChangeHeaderPatch.execute(context)
|
||||
}
|
||||
|
||||
@@ -27,8 +27,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -37,8 +37,12 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -28,8 +28,12 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -34,8 +34,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -32,8 +32,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -27,8 +27,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -27,8 +27,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -26,8 +26,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -27,8 +27,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -30,8 +30,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -27,8 +27,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -23,8 +23,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -23,8 +23,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -37,8 +37,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -97,7 +101,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
"revanced_hide_search_result_recommendations",
|
||||
StringResource(
|
||||
"revanced_hide_search_result_recommendations_title",
|
||||
"Hide search result recommendations (e.g People also watched)"
|
||||
"Hide \\\'People also watched\\\' recommendations"
|
||||
),
|
||||
StringResource(
|
||||
"revanced_hide_search_result_recommendations_summary_on",
|
||||
|
||||
@@ -34,8 +34,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -23,8 +23,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -23,8 +23,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -20,15 +20,21 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
SettingsPatch::class
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage("com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
])
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -25,8 +25,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"com.google.android.youtube", [
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -31,8 +31,12 @@ import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -35,8 +35,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -56,6 +60,8 @@ object HideShortsComponentsPatch : BytecodePatch(
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// region Hide the Shorts shelf.
|
||||
|
||||
// This patch point is not present in 19.03.x and greater.
|
||||
// If 19.02.x and lower is dropped, then this section of code and the fingerprint should be removed.
|
||||
ReelConstructorFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2
|
||||
@@ -68,7 +74,7 @@ object HideShortsComponentsPatch : BytecodePatch(
|
||||
"hideShortsShelf"
|
||||
)
|
||||
}
|
||||
} ?: throw ReelConstructorFingerprint.exception
|
||||
} // Do not throw an exception if not resolved.
|
||||
|
||||
// endregion
|
||||
|
||||
|
||||
@@ -97,9 +97,15 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
|
||||
)
|
||||
)
|
||||
|
||||
fun String.getId() = ResourceMappingPatch.resourceMappings.single { it.name == this }.id
|
||||
ResourceMappingPatch.resourceMappings.find {
|
||||
it.type == "layout" && it.name == "reel_multiple_items_shelf"
|
||||
}?.also {
|
||||
reelMultipleItemShelfId = it.id
|
||||
}
|
||||
|
||||
reelMultipleItemShelfId = "reel_multiple_items_shelf".getId()
|
||||
reelPlayerRightCellButtonHeight = "reel_player_right_cell_button_height".getId()
|
||||
reelPlayerRightCellButtonHeight =
|
||||
ResourceMappingPatch.resourceMappings.single {
|
||||
it.type == "dimen" && it.name == "reel_player_right_cell_button_height"
|
||||
}.id
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import app.revanced.util.containsWideLiteralInstructionValue
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object ReelConstructorFingerprint : LiteralValueFingerprint(
|
||||
internal object ReelConstructorFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
opcodes = listOf(Opcode.INVOKE_VIRTUAL),
|
||||
literalSupplier = { HideShortsComponentsResourcePatch.reelMultipleItemShelfId }
|
||||
customFingerprint = { methodDef, _ ->
|
||||
// Cannot use LiteralValueFingerprint, because the resource id may not be present.
|
||||
val reelMultipleItemShelfId = HideShortsComponentsResourcePatch.reelMultipleItemShelfId
|
||||
reelMultipleItemShelfId != -1L
|
||||
&& methodDef.containsWideLiteralInstructionValue(reelMultipleItemShelfId)
|
||||
}
|
||||
)
|
||||
@@ -22,8 +22,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -19,13 +19,16 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -24,8 +24,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -18,8 +18,12 @@ import org.w3c.dom.Element
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
],
|
||||
|
||||
@@ -1,29 +1,45 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.*
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.ConversionContextFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.DislikeFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.DislikesOldLayoutTextViewFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.LikeFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RemoveLikeFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberMeasureAnimatedTextFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberMeasureStaticLabelFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberMeasureStaticLabelParentFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberSetterFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberTextViewFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.ShortsTextViewFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.TextComponentConstructorFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.TextComponentDataFingerprint
|
||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.TextComponentLookupFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint
|
||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
|
||||
@Patch(
|
||||
name = "Return YouTube Dislike",
|
||||
@@ -38,10 +54,10 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube", [
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -49,14 +65,17 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
@Suppress("unused")
|
||||
object ReturnYouTubeDislikePatch : BytecodePatch(
|
||||
setOf(
|
||||
ConversionContextFingerprint,
|
||||
TextComponentConstructorFingerprint,
|
||||
TextComponentDataFingerprint,
|
||||
ShortsTextViewFingerprint,
|
||||
DislikesOldLayoutTextViewFingerprint,
|
||||
LikeFingerprint,
|
||||
DislikeFingerprint,
|
||||
RemoveLikeFingerprint,
|
||||
RollingNumberSetterFingerprint,
|
||||
RollingNumberMeasureTextParentFingerprint,
|
||||
RollingNumberMeasureStaticLabelParentFingerprint,
|
||||
RollingNumberMeasureAnimatedTextFingerprint,
|
||||
RollingNumberTextViewFingerprint,
|
||||
RollingNumberTextViewAnimationUpdateFingerprint
|
||||
)
|
||||
@@ -67,6 +86,8 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
||||
private const val FILTER_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch;"
|
||||
|
||||
private fun MethodFingerprint.resultOrThrow() = result ?: throw exception
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// region Inject newVideoLoaded event handler to update dislikes when a new video is loaded.
|
||||
|
||||
@@ -97,174 +118,54 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hook creation of Spans and the cached lookup of them.
|
||||
// region Hook code for creation and cached lookup of text Spans.
|
||||
|
||||
// Alternatively the hook can be made at the creation of Spans in TextComponentSpec,
|
||||
// And it works in all situations except it fails to update the Span when the user dislikes,
|
||||
// since the underlying (likes only) text did not change.
|
||||
// This hook handles all situations, as it's where the created Spans are stored and later reused.
|
||||
TextComponentContextFingerprint.also {
|
||||
if (!it.resolve(context, TextComponentConstructorFingerprint.result!!.classDef))
|
||||
throw it.exception
|
||||
}.result?.also { result ->
|
||||
if (!TextComponentAtomicReferenceFingerprint.resolve(context, result.method, result.classDef))
|
||||
throw TextComponentAtomicReferenceFingerprint.exception
|
||||
}?.let { textComponentContextFingerprintResult ->
|
||||
val conversionContextIndex = textComponentContextFingerprintResult
|
||||
.scanResult.patternScanResult!!.endIndex
|
||||
val atomicReferenceStartIndex = TextComponentAtomicReferenceFingerprint.result!!
|
||||
.scanResult.patternScanResult!!.startIndex
|
||||
TextComponentConstructorFingerprint.result?.let { textConstructorResult ->
|
||||
// Find the field name of the conversion context.
|
||||
val conversionContextClassType = ConversionContextFingerprint.resultOrThrow().classDef.type
|
||||
val conversionContextField = textConstructorResult.classDef.fields.find {
|
||||
it.type == conversionContextClassType
|
||||
} ?: throw PatchException("Could not find conversion context field")
|
||||
|
||||
val insertIndex = atomicReferenceStartIndex + 9
|
||||
TextComponentLookupFingerprint.resolve(context, textConstructorResult.classDef)
|
||||
TextComponentLookupFingerprint.resultOrThrow().mutableMethod.apply {
|
||||
// Find the instruction for creating the text data object.
|
||||
val textDataClassType = TextComponentDataFingerprint.resultOrThrow().classDef.type
|
||||
val insertIndex = indexOfFirstInstruction {
|
||||
opcode == Opcode.NEW_INSTANCE &&
|
||||
getReference<TypeReference>()?.type == textDataClassType
|
||||
}
|
||||
if (insertIndex < 0) throw PatchException("Could not find data creation instruction")
|
||||
val tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
textComponentContextFingerprintResult.mutableMethod.apply {
|
||||
// Get the conversion context obfuscated field name
|
||||
val conversionContextFieldReference =
|
||||
getInstruction<ReferenceInstruction>(conversionContextIndex).reference
|
||||
|
||||
// Free register to hold the conversion context
|
||||
val freeRegister =
|
||||
getInstruction<TwoRegisterInstruction>(atomicReferenceStartIndex).registerB
|
||||
|
||||
val atomicReferenceRegister =
|
||||
getInstruction<FiveRegisterInstruction>(atomicReferenceStartIndex + 6).registerC
|
||||
|
||||
// Instruction that is replaced, and also has the CharacterSequence register.
|
||||
val moveCharSequenceInstruction = getInstruction<TwoRegisterInstruction>(insertIndex)
|
||||
val charSequenceSourceRegister = moveCharSequenceInstruction.registerB
|
||||
val charSequenceTargetRegister = moveCharSequenceInstruction.registerA
|
||||
|
||||
// Move the current instance to the free register, and get the conversion context from it.
|
||||
// Must replace the instruction to preserve the control flow label.
|
||||
replaceInstruction(insertIndex, "move-object/from16 v$freeRegister, p0")
|
||||
addInstructions(
|
||||
insertIndex + 1,
|
||||
"""
|
||||
# Move context to free register
|
||||
iget-object v$freeRegister, v$freeRegister, $conversionContextFieldReference
|
||||
invoke-static {v$freeRegister, v$atomicReferenceRegister, v$charSequenceSourceRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/util/concurrent/atomic/AtomicReference;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$freeRegister
|
||||
# Replace the original instruction
|
||||
move-object v${charSequenceTargetRegister}, v${freeRegister}
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw TextComponentContextFingerprint.exception
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hook rolling numbers.
|
||||
|
||||
RollingNumberSetterFingerprint.result?.let {
|
||||
val dislikesIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = 1
|
||||
|
||||
val charSequenceInstanceRegister =
|
||||
getInstruction<OneRegisterInstruction>(0).registerA
|
||||
val charSequenceFieldReference =
|
||||
getInstruction<ReferenceInstruction>(dislikesIndex).reference.toString()
|
||||
|
||||
val registerCount = implementation!!.registerCount
|
||||
|
||||
// This register is being overwritten, so it is free to use.
|
||||
val freeRegister = registerCount - 1
|
||||
val conversionContextRegister = registerCount - parameters.size + 1
|
||||
// Find the instruction that sets the span to an instance field.
|
||||
// The instruction is only a few lines after the creation of the instance.
|
||||
// The method has multiple iput-object instructions using a CharSequence,
|
||||
// so verify the found instruction is in the expected location.
|
||||
val putFieldInstruction = implementation!!.instructions
|
||||
.subList(insertIndex, insertIndex + 20)
|
||||
.find {
|
||||
it.opcode == Opcode.IPUT_OBJECT &&
|
||||
it.getReference<FieldReference>()?.type == "Ljava/lang/CharSequence;"
|
||||
} ?: throw PatchException("Could not find put object instruction")
|
||||
val charSequenceRegister = (putFieldInstruction as TwoRegisterInstruction).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
iget-object v$freeRegister, v$charSequenceInstanceRegister, $charSequenceFieldReference
|
||||
invoke-static {v$conversionContextRegister, v$freeRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onRollingNumberLoaded(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$freeRegister
|
||||
iput-object v$freeRegister, v$charSequenceInstanceRegister, $charSequenceFieldReference
|
||||
"""
|
||||
# Copy conversion context
|
||||
move-object/from16 v$tempRegister, p0
|
||||
iget-object v$tempRegister, v$tempRegister, $conversionContextField
|
||||
invoke-static {v$tempRegister, v$charSequenceRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$charSequenceRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw RollingNumberSetterFingerprint.exception
|
||||
|
||||
// Rolling Number text views use the measured width of the raw string for layout.
|
||||
// Modify the measure text calculation to include the left drawable separator if needed.
|
||||
RollingNumberMeasureAnimatedTextFingerprint.also {
|
||||
if (!it.resolve(context, RollingNumberMeasureTextParentFingerprint.result!!.classDef))
|
||||
throw it.exception
|
||||
}.result?.also {
|
||||
it.mutableMethod.apply {
|
||||
val returnInstructionIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
val measuredTextWidthRegister =
|
||||
getInstruction<OneRegisterInstruction>(returnInstructionIndex).registerA
|
||||
|
||||
replaceInstruction( // Replace instruction to preserve control flow label.
|
||||
returnInstructionIndex,
|
||||
"invoke-static {p1, v$measuredTextWidthRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F"
|
||||
)
|
||||
addInstructions(
|
||||
returnInstructionIndex + 1,
|
||||
"""
|
||||
move-result v$measuredTextWidthRegister
|
||||
return v$measuredTextWidthRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw RollingNumberMeasureAnimatedTextFingerprint.exception
|
||||
|
||||
// Additional text measurement method. Used if YouTube decides not to animate the likes count
|
||||
// and sometimes used for initial video load.
|
||||
RollingNumberMeasureStaticLabelFingerprint.also {
|
||||
if (!it.resolve(context, RollingNumberMeasureTextParentFingerprint.result!!.classDef))
|
||||
throw it.exception
|
||||
}.result?.also {
|
||||
it.mutableMethod.apply {
|
||||
val measureTextIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
||||
val freeRegister = getInstruction<TwoRegisterInstruction>(0).registerA
|
||||
|
||||
addInstructions(
|
||||
measureTextIndex + 1,
|
||||
"""
|
||||
move-result v$freeRegister
|
||||
invoke-static {p1, v$freeRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw RollingNumberMeasureStaticLabelFingerprint.exception
|
||||
|
||||
// The rolling number Span is missing styling since it's initially set as a String.
|
||||
// Modify the UI text view and use the styled like/dislike Span.
|
||||
RollingNumberTextViewFingerprint.result?.let {
|
||||
// Initial TextView is set in this method.
|
||||
val initiallyCreatedTextViewMethod = it.mutableMethod
|
||||
|
||||
// Videos less than 24 hours after uploaded, like counts will be updated in real time.
|
||||
// Whenever like counts are updated, TextView is set in this method.
|
||||
val realTimeUpdateTextViewMethod =
|
||||
RollingNumberTextViewAnimationUpdateFingerprint.result?.mutableMethod
|
||||
?: throw RollingNumberTextViewAnimationUpdateFingerprint.exception
|
||||
|
||||
arrayOf(
|
||||
initiallyCreatedTextViewMethod,
|
||||
realTimeUpdateTextViewMethod
|
||||
).forEach { insertMethod ->
|
||||
insertMethod.apply {
|
||||
val setTextIndex = indexOfFirstInstruction {
|
||||
getReference<MethodReference>()?.name == "setText"
|
||||
}
|
||||
|
||||
val textViewRegister =
|
||||
getInstruction<FiveRegisterInstruction>(setTextIndex).registerC
|
||||
val textSpanRegister =
|
||||
getInstruction<FiveRegisterInstruction>(setTextIndex).registerD
|
||||
|
||||
addInstructions(
|
||||
setTextIndex,
|
||||
"""
|
||||
invoke-static {v$textViewRegister, v$textSpanRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->updateRollingNumber(Landroid/widget/TextView;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$textSpanRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
} ?: throw RollingNumberTextViewFingerprint.exception
|
||||
} ?: throw TextComponentConstructorFingerprint.exception
|
||||
|
||||
// endregion
|
||||
|
||||
@@ -335,6 +236,120 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
||||
} ?: throw DislikesOldLayoutTextViewFingerprint.exception
|
||||
|
||||
// endregion
|
||||
|
||||
|
||||
// region Hook rolling numbers.
|
||||
|
||||
// Do this last to allow patching old unsupported versions (if the user really wants),
|
||||
// On older unsupported version this will fail to resolve and throw an exception,
|
||||
// but everything will still work correctly anyways.
|
||||
|
||||
RollingNumberSetterFingerprint.result?.let {
|
||||
val dislikesIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = 1
|
||||
|
||||
val charSequenceInstanceRegister =
|
||||
getInstruction<OneRegisterInstruction>(0).registerA
|
||||
val charSequenceFieldReference =
|
||||
getInstruction<ReferenceInstruction>(dislikesIndex).reference
|
||||
|
||||
val registerCount = implementation!!.registerCount
|
||||
|
||||
// This register is being overwritten, so it is free to use.
|
||||
val freeRegister = registerCount - 1
|
||||
val conversionContextRegister = registerCount - parameters.size + 1
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
iget-object v$freeRegister, v$charSequenceInstanceRegister, $charSequenceFieldReference
|
||||
invoke-static {v$conversionContextRegister, v$freeRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onRollingNumberLoaded(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$freeRegister
|
||||
iput-object v$freeRegister, v$charSequenceInstanceRegister, $charSequenceFieldReference
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw RollingNumberSetterFingerprint.exception
|
||||
|
||||
// Rolling Number text views use the measured width of the raw string for layout.
|
||||
// Modify the measure text calculation to include the left drawable separator if needed.
|
||||
RollingNumberMeasureAnimatedTextFingerprint.result?.also {
|
||||
val scanResult = it.scanResult.patternScanResult!!
|
||||
// Additional check to verify the opcodes are at the start of the method
|
||||
if (scanResult.startIndex != 0) throw PatchException("Unexpected opcode location")
|
||||
val endIndex = scanResult.endIndex
|
||||
it.mutableMethod.apply {
|
||||
val measuredTextWidthRegister = getInstruction<OneRegisterInstruction>(endIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
endIndex + 1,
|
||||
"""
|
||||
invoke-static {p1, v$measuredTextWidthRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F
|
||||
move-result v$measuredTextWidthRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw RollingNumberMeasureAnimatedTextFingerprint.exception
|
||||
|
||||
// Additional text measurement method. Used if YouTube decides not to animate the likes count
|
||||
// and sometimes used for initial video load.
|
||||
RollingNumberMeasureStaticLabelFingerprint.resolve(context, RollingNumberMeasureStaticLabelParentFingerprint.resultOrThrow().classDef)
|
||||
RollingNumberMeasureStaticLabelFingerprint.result?.also {
|
||||
val measureTextIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
||||
it.mutableMethod.apply {
|
||||
val freeRegister = getInstruction<TwoRegisterInstruction>(0).registerA
|
||||
|
||||
addInstructions(
|
||||
measureTextIndex + 1,
|
||||
"""
|
||||
move-result v$freeRegister
|
||||
invoke-static {p1, v$freeRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw RollingNumberMeasureStaticLabelFingerprint.exception
|
||||
|
||||
// The rolling number Span is missing styling since it's initially set as a String.
|
||||
// Modify the UI text view and use the styled like/dislike Span.
|
||||
RollingNumberTextViewFingerprint.result?.let {
|
||||
// Initial TextView is set in this method.
|
||||
val initiallyCreatedTextViewMethod = it.mutableMethod
|
||||
|
||||
// Videos less than 24 hours after uploaded, like counts will be updated in real time.
|
||||
// Whenever like counts are updated, TextView is set in this method.
|
||||
val realTimeUpdateTextViewMethod =
|
||||
RollingNumberTextViewAnimationUpdateFingerprint.result?.mutableMethod
|
||||
?: throw RollingNumberTextViewAnimationUpdateFingerprint.exception
|
||||
|
||||
arrayOf(
|
||||
initiallyCreatedTextViewMethod,
|
||||
realTimeUpdateTextViewMethod
|
||||
).forEach { insertMethod ->
|
||||
insertMethod.apply {
|
||||
val setTextIndex = indexOfFirstInstruction {
|
||||
getReference<MethodReference>()?.name == "setText"
|
||||
}
|
||||
|
||||
val textViewRegister =
|
||||
getInstruction<FiveRegisterInstruction>(setTextIndex).registerC
|
||||
val textSpanRegister =
|
||||
getInstruction<FiveRegisterInstruction>(setTextIndex).registerD
|
||||
|
||||
addInstructions(
|
||||
setTextIndex,
|
||||
"""
|
||||
invoke-static {v$textViewRegister, v$textSpanRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->updateRollingNumber(Landroid/widget/TextView;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$textSpanRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
} ?: throw RollingNumberTextViewFingerprint.exception
|
||||
|
||||
// endregion
|
||||
|
||||
}
|
||||
|
||||
private fun MethodFingerprint.toPatch(voteKind: Vote) = VotePatch(this, voteKind)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object ConversionContextFingerprint : MethodFingerprint(
|
||||
returnType = "Ljava/lang/String;",
|
||||
parameters = listOf(),
|
||||
strings = listOf(
|
||||
", widthConstraint=",
|
||||
", heightConstraint=",
|
||||
", templateLoggerFactory=",
|
||||
", rootDisposableContainer=",
|
||||
// 18.37.36 and after this String is: ConversionContext{containerInternal=
|
||||
// and before it is: ConversionContext{container=
|
||||
// Use a partial string to match both.
|
||||
"ConversionContext{container"
|
||||
)
|
||||
)
|
||||
@@ -5,19 +5,24 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Resolves to class found in [RollingNumberMeasureTextParentFingerprint].
|
||||
*/
|
||||
internal object RollingNumberMeasureAnimatedTextFingerprint : MethodFingerprint(
|
||||
returnType = "F",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Ljava/lang/String;"),
|
||||
returnType = "Lj\$/util/Optional;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
parameters = listOf(
|
||||
"L",
|
||||
"Ljava/lang/String;",
|
||||
"L"
|
||||
),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET, // First instruction of method
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_HIGH16,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.ADD_FLOAT_2ADDR,
|
||||
Opcode.ADD_INT_LIT8,
|
||||
Opcode.GOTO,
|
||||
Opcode.RETURN
|
||||
Opcode.CONST_4,
|
||||
Opcode.AGET,
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_4, // Measured text width
|
||||
)
|
||||
)
|
||||
@@ -6,7 +6,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Resolves to class found in [RollingNumberMeasureTextParentFingerprint].
|
||||
* Resolves to class found in [RollingNumberMeasureStaticLabelParentFingerprint].
|
||||
*/
|
||||
internal object RollingNumberMeasureStaticLabelFingerprint : MethodFingerprint(
|
||||
returnType = "F",
|
||||
|
||||
@@ -4,7 +4,7 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object RollingNumberMeasureTextParentFingerprint : MethodFingerprint(
|
||||
internal object RollingNumberMeasureStaticLabelParentFingerprint : MethodFingerprint(
|
||||
returnType = "Ljava/lang/String;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf(),
|
||||
@@ -1,33 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Resolves against the same method that [TextComponentContextFingerprint] resolves to.
|
||||
*/
|
||||
internal object TextComponentAtomicReferenceFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_OBJECT, // Register B is free register
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
null,
|
||||
Opcode.INVOKE_VIRTUAL, // Register C is atomic reference
|
||||
Opcode.MOVE_RESULT_OBJECT, // Register A is char sequence
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.MOVE_OBJECT, // Replace this instruction with patch code
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.GOTO
|
||||
)
|
||||
)
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Resolves against the same class that [TextComponentConstructorFingerprint] resolves to.
|
||||
*/
|
||||
internal object TextComponentContextFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.INVOKE_STATIC_RANGE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT, // conversion context field name
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object TextComponentDataFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("L", "L"),
|
||||
strings = listOf("text"),
|
||||
customFingerprint = { _, classDef ->
|
||||
val fields = classDef.fields
|
||||
fields.find { it.type == "Ljava/util/BitSet;" } != null &&
|
||||
fields.find { it.type == "[Ljava/lang/String;" } != null
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
/**
|
||||
* Resolves against the same class that [TextComponentConstructorFingerprint] resolves to.
|
||||
*/
|
||||
internal object TextComponentLookupFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||
parameters = listOf("L"),
|
||||
strings = listOf("…")
|
||||
)
|
||||
@@ -26,8 +26,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -23,8 +23,12 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -38,13 +38,11 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
],
|
||||
|
||||
@@ -28,8 +28,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -65,7 +69,7 @@ object SpoofAppVersionPatch : BytecodePatch(
|
||||
StringResource("revanced_spoof_app_version_target_entry_2", "18.20.39 - Restore wide video speed & quality menu"),
|
||||
StringResource("revanced_spoof_app_version_target_entry_3", "17.08.35 - Restore old UI layout"),
|
||||
StringResource("revanced_spoof_app_version_target_entry_4", "16.08.35 - Restore explore tab"),
|
||||
StringResource("revanced_spoof_app_version_target_entry_5", "16.01.35 - Restore old Shorts player"),
|
||||
StringResource("revanced_spoof_app_version_target_entry_5", "16.01.35 - Restore fewer video player action buttons"),
|
||||
)
|
||||
),
|
||||
ArrayResource(
|
||||
|
||||
@@ -24,8 +24,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -32,8 +32,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@@ -35,8 +35,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -40,8 +40,12 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -10,7 +10,7 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.shared.settings.preference.impl.StringResource
|
||||
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.WatchWhileActivityFingerprint
|
||||
import app.revanced.patches.youtube.shared.fingerprints.MainActivityFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Patch(
|
||||
@@ -21,15 +21,15 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
)
|
||||
@Suppress("unused")
|
||||
object AnnouncementsPatch : BytecodePatch(
|
||||
setOf(WatchWhileActivityFingerprint)
|
||||
setOf(MainActivityFingerprint)
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/announcements/AnnouncementsPatch;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val onCreateMethod = WatchWhileActivityFingerprint.result?.let {
|
||||
val onCreateMethod = MainActivityFingerprint.result?.let {
|
||||
it.mutableClass.methods.find { method -> method.name == "onCreate" }
|
||||
} ?: throw WatchWhileActivityFingerprint.exception
|
||||
} ?: throw MainActivityFingerprint.exception
|
||||
|
||||
val superCallIndex = onCreateMethod.getInstructions().indexOfFirst { it.opcode == Opcode.INVOKE_SUPER_RANGE }
|
||||
|
||||
@@ -43,7 +43,7 @@ object AnnouncementsPatch : BytecodePatch(
|
||||
"revanced_announcements",
|
||||
StringResource(
|
||||
"revanced_announcements_title",
|
||||
"Show announcements from ReVanced"
|
||||
"Show ReVanced announcements"
|
||||
),
|
||||
StringResource(
|
||||
"revanced_announcements_summary_on",
|
||||
|
||||
@@ -28,8 +28,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -23,12 +23,17 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object SpoofDeviceDimensionsPatch : BytecodePatch(
|
||||
setOf(DeviceDimensionsModelToStringFingerprint)
|
||||
) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.revanced.patches.youtube.misc.fix.backtoexitgesture
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
@@ -10,6 +9,7 @@ import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fingerprints.OnBa
|
||||
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fingerprints.RecyclerViewScrollingFingerprint
|
||||
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fingerprints.RecyclerViewTopScrollingFingerprint
|
||||
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fingerprints.RecyclerViewTopScrollingParentFingerprint
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(description = "Fixes the swipe back to exit gesture.")
|
||||
@Suppress("unused")
|
||||
@@ -37,7 +37,7 @@ internal object FixBackToExitGesturePatch : BytecodePatch(
|
||||
methodName = "onScrollingViews"
|
||||
),
|
||||
OnBackPressedFingerprint to IntegrationsMethod(
|
||||
"p0", "onBackPressed", "Lcom/google/android/apps/youtube/app/watchwhile/WatchWhileActivity;"
|
||||
"p0", "onBackPressed", "Landroid/app/Activity;"
|
||||
)
|
||||
).forEach { (fingerprint, target) -> fingerprint.injectCall(target) }
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ internal object OnBackPressedFingerprint : MethodFingerprint(
|
||||
Opcode.RETURN_VOID
|
||||
),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.definingClass.endsWith("WatchWhileActivity;")
|
||||
(methodDef.definingClass.endsWith("MainActivity;") ||
|
||||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|
||||
methodDef.definingClass.endsWith("WatchWhileActivity;"))
|
||||
&& methodDef.name == "onBackPressed"
|
||||
}
|
||||
)
|
||||
@@ -10,22 +10,18 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.UserAgentHeaderBuilderFingerprint
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
|
||||
|
||||
@Patch(
|
||||
name = "Client spoof",
|
||||
description = "Adds options to spoof the client to allow video playback.",
|
||||
dependencies = [SpoofSignaturePatch::class],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"com.google.android.youtube", [
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -33,13 +33,11 @@ object GmsCoreSupportPatch : AbstractGmsCoreSupportPatch(
|
||||
compatiblePackages = setOf(
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube", setOf(
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -25,12 +25,17 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
[
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object BypassURLRedirectsPatch : BytecodePatch(
|
||||
setOf(ABUriParserFingerprint, HTTPUriParserFingerprint)
|
||||
) {
|
||||
|
||||
@@ -28,8 +28,12 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -34,13 +34,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -30,8 +30,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
[
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -4,10 +4,12 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object WatchWhileActivityFingerprint : MethodFingerprint(
|
||||
internal object MainActivityFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf(),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.definingClass.endsWith("WatchWhileActivity;")
|
||||
methodDef.definingClass.endsWith("MainActivity;")
|
||||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
|
||||
|| methodDef.definingClass.endsWith("WatchWhileActivity;")
|
||||
}
|
||||
)
|
||||
@@ -26,8 +26,12 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.45.43",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.revanced.patches.youtube.video.information
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
@@ -14,13 +13,14 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.video.information.fingerprints.*
|
||||
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
@@ -135,23 +135,24 @@ object VideoInformationPatch : BytecodePatch(
|
||||
*/
|
||||
videoTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime")
|
||||
|
||||
|
||||
/*
|
||||
* Hook the user playback speed selection
|
||||
*/
|
||||
OnPlaybackSpeedItemClickFingerprint.result?.apply {
|
||||
speedSelectionInsertMethod = mutableMethod
|
||||
speedSelectionInsertIndex = scanResult.patternScanResult!!.startIndex - 3
|
||||
OnPlaybackSpeedItemClickFingerprint.result?.mutableMethod?.apply {
|
||||
speedSelectionInsertMethod = this
|
||||
val speedSelectionMethodInstructions = this.implementation!!.instructions
|
||||
val speedSelectionValueInstructionIndex = speedSelectionMethodInstructions.indexOfFirst {
|
||||
it.opcode == Opcode.IGET
|
||||
}
|
||||
speedSelectionValueRegister =
|
||||
mutableMethod.getInstruction<FiveRegisterInstruction>(speedSelectionInsertIndex).registerD
|
||||
|
||||
val speedSelectionMethodInstructions = mutableMethod.implementation!!.instructions
|
||||
getInstruction<TwoRegisterInstruction>(speedSelectionValueInstructionIndex).registerA
|
||||
setPlaybackSpeedClassFieldReference =
|
||||
getInstruction<ReferenceInstruction>(speedSelectionValueInstructionIndex + 1).reference.toString()
|
||||
setPlaybackSpeedMethodReference =
|
||||
getInstruction<ReferenceInstruction>(speedSelectionValueInstructionIndex + 2).reference.toString()
|
||||
setPlaybackSpeedContainerClassFieldReference =
|
||||
getReference(speedSelectionMethodInstructions, -1, Opcode.IF_EQZ)
|
||||
setPlaybackSpeedClassFieldReference =
|
||||
getReference(speedSelectionMethodInstructions, 1, Opcode.IGET)
|
||||
setPlaybackSpeedMethodReference =
|
||||
getReference(speedSelectionMethodInstructions, 2, Opcode.IGET)
|
||||
speedSelectionInsertIndex = speedSelectionValueInstructionIndex + 1
|
||||
} ?: throw OnPlaybackSpeedItemClickFingerprint.exception
|
||||
|
||||
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
|
||||
|
||||
@@ -2,18 +2,19 @@ package app.revanced.patches.youtube.video.information.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
|
||||
internal object OnPlaybackSpeedItemClickFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf("L", "L", "I", "J"),
|
||||
customFingerprint = { methodDef, _ -> methodDef.name == "onItemClick" },
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.name == "onItemClick" && methodDef.implementation?.instructions?.find {
|
||||
it.opcode == Opcode.IGET_OBJECT &&
|
||||
it.getReference<FieldReference>()!!.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"
|
||||
} != null
|
||||
}
|
||||
)
|
||||
@@ -18,29 +18,44 @@ object PlayerResponseMethodHookPatch :
|
||||
BytecodePatch(setOf(PlayerParameterBuilderFingerprint)),
|
||||
Closeable,
|
||||
MutableSet<PlayerResponseMethodHookPatch.Hook> by mutableSetOf() {
|
||||
private const val VIDEO_ID_PARAMETER = 1
|
||||
private const val IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
|
||||
private const val PROTO_BUFFER_PARAMETER_PARAMETER = 3
|
||||
|
||||
// Parameter numbers of the patched method.
|
||||
private const val PARAMETER_VIDEO_ID = 1
|
||||
private const val PARAMETER_PROTO_BUFFER = 3
|
||||
private const val PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING = 11
|
||||
|
||||
// Temporary 4-bit registers used to pass the parameters to integrations.
|
||||
private const val REGISTER_VIDEO_ID = 0
|
||||
private const val REGISTER_PROTO_BUFFER = 1
|
||||
private const val REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING = 2
|
||||
|
||||
private lateinit var playerResponseMethod: MutableMethod
|
||||
|
||||
private var numberOfInstructionsAdded = 0
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
playerResponseMethod = PlayerParameterBuilderFingerprint.result?.mutableMethod
|
||||
?: throw PlayerParameterBuilderFingerprint.exception
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction(
|
||||
0, "invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
|
||||
)
|
||||
fun hookVideoId(hook: Hook) {
|
||||
playerResponseMethod.addInstruction(
|
||||
0, "invoke-static {v$REGISTER_VIDEO_ID, v$REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook"
|
||||
)
|
||||
numberOfInstructionsAdded++
|
||||
}
|
||||
|
||||
fun hookProtoBufferParameter(hook: Hook) = playerResponseMethod.addInstructions(
|
||||
0,
|
||||
fun hookProtoBufferParameter(hook: Hook) {
|
||||
playerResponseMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static {v$REGISTER_PROTO_BUFFER, v$REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook
|
||||
move-result-object v$REGISTER_PROTO_BUFFER
|
||||
"""
|
||||
invoke-static {p$PROTO_BUFFER_PARAMETER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
|
||||
move-result-object p$PROTO_BUFFER_PARAMETER_PARAMETER
|
||||
"""
|
||||
)
|
||||
)
|
||||
numberOfInstructionsAdded += 2
|
||||
}
|
||||
|
||||
// Reverse the order in order to preserve insertion order of the hooks.
|
||||
val beforeVideoIdHooks = filterIsInstance<Hook.ProtoBufferParameterBeforeVideoId>().asReversed()
|
||||
@@ -51,6 +66,23 @@ object PlayerResponseMethodHookPatch :
|
||||
afterVideoIdHooks.forEach(::hookProtoBufferParameter)
|
||||
videoIdHooks.forEach(::hookVideoId)
|
||||
beforeVideoIdHooks.forEach(::hookProtoBufferParameter)
|
||||
|
||||
// On some app targets the method has too many registers pushing the parameters past v15.
|
||||
// Move the parameters to 4-bit registers so they can be passed to integrations.
|
||||
playerResponseMethod.addInstructions(
|
||||
0, """
|
||||
move-object/from16 v$REGISTER_VIDEO_ID, p$PARAMETER_VIDEO_ID
|
||||
move-object/from16 v$REGISTER_PROTO_BUFFER, p$PARAMETER_PROTO_BUFFER
|
||||
move/from16 v$REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING, p$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING
|
||||
""",
|
||||
)
|
||||
numberOfInstructionsAdded += 3
|
||||
|
||||
// Move the modified register back.
|
||||
playerResponseMethod.addInstruction(
|
||||
numberOfInstructionsAdded,
|
||||
"move-object/from16 p$PARAMETER_PROTO_BUFFER, v$REGISTER_PROTO_BUFFER"
|
||||
)
|
||||
}
|
||||
|
||||
internal abstract class Hook(private val methodDescriptor: String) {
|
||||
|
||||
@@ -31,13 +31,11 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -14,13 +14,11 @@ import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPa
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube", [
|
||||
"18.32.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.41",
|
||||
"18.45.43"
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39",
|
||||
"19.03.35"
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@@ -29,7 +29,13 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
|
||||
@Patch(
|
||||
description = "Adds custom playback speed options.",
|
||||
dependencies = [IntegrationsPatch::class, LithoFilterPatch::class, SettingsPatch::class, RecyclerViewTreeHookPatch::class]
|
||||
dependencies = [
|
||||
IntegrationsPatch::class,
|
||||
LithoFilterPatch::class,
|
||||
SettingsPatch::class,
|
||||
RecyclerViewTreeHookPatch::class,
|
||||
CustomPlaybackSpeedResourcePatch::class
|
||||
]
|
||||
)
|
||||
object CustomPlaybackSpeedPatch : BytecodePatch(
|
||||
setOf(
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package app.revanced.patches.youtube.video.speed.custom
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patches.shared.mapping.misc.ResourceMappingPatch
|
||||
|
||||
internal object CustomPlaybackSpeedResourcePatch : ResourcePatch() {
|
||||
var speedUnavailableId: Long = -1
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
speedUnavailableId = ResourceMappingPatch.resourceMappings.single {
|
||||
it.type == "string" && it.name == "varispeed_unavailable_message"
|
||||
}.id
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package app.revanced.patches.youtube.video.speed.custom.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
|
||||
internal object ShowOldPlaybackSpeedMenuFingerprint : MethodFingerprint(
|
||||
strings = listOf("PLAYBACK_RATE_MENU_BOTTOM_SHEET_FRAGMENT")
|
||||
internal object ShowOldPlaybackSpeedMenuFingerprint : LiteralValueFingerprint(
|
||||
literalSupplier = {
|
||||
CustomPlaybackSpeedResourcePatch.speedUnavailableId
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.revanced.patches.youtube.video.videoid
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
@@ -13,6 +12,7 @@ import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||
import app.revanced.patches.youtube.video.videoid.fingerprint.VideoIdFingerprint
|
||||
import app.revanced.patches.youtube.video.videoid.fingerprint.VideoIdFingerprintBackgroundPlay
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
@@ -49,7 +49,7 @@ object VideoIdPatch : BytecodePatch(
|
||||
consumer(it, insertIndex, videoIdRegister)
|
||||
|
||||
}
|
||||
} ?: throw VideoIdFingerprint.exception
|
||||
} ?: throw exception
|
||||
|
||||
VideoIdFingerprint.setFields { method, index, register ->
|
||||
videoIdMethod = method
|
||||
|
||||
@@ -10,19 +10,13 @@ internal object VideoIdFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -10,18 +10,10 @@ internal object VideoIdFingerprintBackgroundPlay : MethodFingerprint(
|
||||
accessFlags = AccessFlags.DECLARED_SYNCHRONIZED or AccessFlags.FINAL or AccessFlags.PUBLIC,
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.MONITOR_EXIT,
|
||||
Opcode.RETURN_VOID,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.NEW_ARRAY,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.APUT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user