mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-28 13:11:03 +00:00
some more progress
This commit is contained in:
@@ -12,15 +12,15 @@ val `Disable Pairip license check` by creatingBytecodePatch(
|
||||
) {
|
||||
|
||||
apply {
|
||||
if (processLicenseResponseMethodOrNull == null || validateLicenseResponseMethodOrNull == null) {
|
||||
if (processLicenseResponseMethod == null || validateLicenseResponseMethod == null) {
|
||||
return@apply Logger.getLogger(this::class.java.name)
|
||||
.warning("Could not find Pairip licensing check. No changes applied.")
|
||||
}
|
||||
|
||||
// Set first parameter (responseCode) to 0 (success status).
|
||||
processLicenseResponseMethod.addInstruction(0, "const/4 p1, 0x0")
|
||||
processLicenseResponseMethod!!.addInstruction(0, "const/4 p1, 0x0")
|
||||
|
||||
// Short-circuit the license response validation.
|
||||
validateLicenseResponseMethod.returnEarly()
|
||||
validateLicenseResponseMethod!!.returnEarly()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package app.revanced.patches.shared.misc.pairip.license
|
||||
|
||||
internal val BytecodePatchContext.processLicenseResponseMethod by gettingFirstMethodDeclaratively {
|
||||
custom { method, classDef ->
|
||||
classDef.type == "Lcom/pairip/licensecheck/LicenseClient;" &&
|
||||
method.name == "processResponse"
|
||||
}
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclarativelyOrNull
|
||||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
|
||||
internal val BytecodePatchContext.processLicenseResponseMethod by gettingFirstMutableMethodDeclarativelyOrNull {
|
||||
name("processResponse")
|
||||
definingClass("Lcom/pairip/licensecheck/LicenseClient;")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.validateLicenseResponseMethod by gettingFirstMethodDeclaratively {
|
||||
custom { method, classDef ->
|
||||
classDef.type == "Lcom/pairip/licensecheck/ResponseValidator;" &&
|
||||
method.name == "validateResponse"
|
||||
}
|
||||
internal val BytecodePatchContext.validateLicenseResponseMethod by gettingFirstMutableMethodDeclarativelyOrNull {
|
||||
name("validateResponse")
|
||||
definingClass("Lcom/pairip/licensecheck/ResponseValidator;")
|
||||
}
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
package app.revanced.patches.shared.misc.settings
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.shared.misc.extension.EXTENSION_CLASS_DESCRIPTOR
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val BytecodePatchContext.themeLightColorResourceNameMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.themeLightColorResourceNameMethod by gettingFirstMutableMethodDeclaratively {
|
||||
name("getThemeLightColorResourceName")
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("Ljava/lang/String;")
|
||||
parameterTypes()
|
||||
custom { method, classDef ->
|
||||
method.name == "getThemeLightColorResourceName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.themeDarkColorResourceNameMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.themeDarkColorResourceNameMethod by gettingFirstMutableMethodDeclaratively {
|
||||
name("getThemeDarkColorResourceName")
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("Ljava/lang/String;")
|
||||
parameterTypes()
|
||||
custom { method, classDef ->
|
||||
method.name == "getThemeDarkColorResourceName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,16 @@ package app.revanced.patches.shared.misc.spoof
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.custom
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.method
|
||||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
@@ -77,12 +80,13 @@ internal val buildRequestMethodMatch = firstMethodComposite {
|
||||
val parameterTypes = parameterTypes
|
||||
val parameterTypesSize = parameterTypes.size
|
||||
(parameterTypesSize == 6 || parameterTypesSize == 7 || parameterTypesSize == 8) &&
|
||||
parameterTypes[1] == "Ljava/util/Map;" && // URL headers.
|
||||
indexOfNewUrlRequestBuilderInstruction(this) >= 0
|
||||
parameterTypes[1] == "Ljava/util/Map;" && // URL headers.
|
||||
indexOfNewUrlRequestBuilderInstruction(this) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.protobufClassParseByteBufferMethod by gettingFirstMethodDeclaratively {
|
||||
name("parseFrom")
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.STATIC)
|
||||
returnType("L")
|
||||
parameterTypes("L", "Ljava/nio/ByteBuffer;")
|
||||
@@ -92,7 +96,6 @@ internal val BytecodePatchContext.protobufClassParseByteBufferMethod by gettingF
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.RETURN_OBJECT,
|
||||
)
|
||||
custom { method, _ -> method.name == "parseFrom" }
|
||||
}
|
||||
|
||||
internal val createStreamingDataMethodMatch = firstMethodComposite {
|
||||
@@ -112,7 +115,7 @@ internal val createStreamingDataMethodMatch = firstMethodComposite {
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.buildMediaDataSourceMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.buildMediaDataSourceMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameterTypes(
|
||||
"Landroid/net/Uri;",
|
||||
@@ -147,7 +150,7 @@ internal val mediaFetchEnumConstructorMethodMatch = firstMethodComposite {
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.nerdsStatsVideoFormatBuilderMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.nerdsStatsVideoFormatBuilderMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returnType("Ljava/lang/String;")
|
||||
parameterTypes("L")
|
||||
@@ -156,12 +159,11 @@ internal val BytecodePatchContext.nerdsStatsVideoFormatBuilderMethod by gettingF
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.patchIncludedExtensionMethodMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.patchIncludedExtensionMethodMethod by gettingFirstMutableMethodDeclaratively {
|
||||
name("isPatchIncluded")
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
custom { method, classDef ->
|
||||
method.name == "isPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
// Feature flag that turns on Platypus programming language code compiled to native C++.
|
||||
@@ -192,9 +194,9 @@ internal fun indexOfNewUrlRequestBuilderInstruction(method: Method) = method.ind
|
||||
val reference = methodReference ?: return@indexOfFirstInstruction false
|
||||
|
||||
opcode == Opcode.INVOKE_VIRTUAL && reference.definingClass == "Lorg/chromium/net/CronetEngine;" &&
|
||||
reference.name == "newUrlRequestBuilder" &&
|
||||
reference.parameterTypes.size == 3 &&
|
||||
reference.parameterTypes[0] == "Ljava/lang/String;" &&
|
||||
reference.parameterTypes[1] == "Lorg/chromium/net/UrlRequest\$Callback;" &&
|
||||
reference.parameterTypes[2] == "Ljava/util/concurrent/Executor;"
|
||||
reference.name == "newUrlRequestBuilder" &&
|
||||
reference.parameterTypes.size == 3 &&
|
||||
reference.parameterTypes[0] == "Ljava/lang/String;" &&
|
||||
reference.parameterTypes[1] == "Lorg/chromium/net/UrlRequest\$Callback;" &&
|
||||
reference.parameterTypes[2] == "Ljava/util/concurrent/Executor;"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package app.revanced.patches.shared.misc.spoof
|
||||
|
||||
import app.revanced.patcher.custom
|
||||
import app.revanced.patcher.extensions.*
|
||||
import app.revanced.patcher.firstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.util.*
|
||||
@@ -124,7 +128,7 @@ internal fun spoofVideoStreamsPatch(
|
||||
addInstruction(
|
||||
videoDetailsIndex + 1,
|
||||
"invoke-direct { p0, v$videoDetailsRegister }, " +
|
||||
"$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V",
|
||||
"$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V",
|
||||
)
|
||||
|
||||
val protobufClass = protobufClassParseByteBufferMethod.definingClass
|
||||
@@ -212,7 +216,7 @@ internal fun spoofVideoStreamsPatch(
|
||||
// A proper fix may include modifying the request body to match the platforms expected body.
|
||||
|
||||
buildMediaDataSourceMethod.apply {
|
||||
val targetIndex = instructions.lastIndex
|
||||
val targetIndex = instructions.count() - 1
|
||||
|
||||
// Instructions are added just before the method returns,
|
||||
// so there's no concern of clobbering in-use registers.
|
||||
@@ -271,7 +275,7 @@ internal fun spoofVideoStreamsPatch(
|
||||
val mediaFetchEnumClass = definingClass
|
||||
val sabrFieldIndex = indexOfFirstInstructionOrThrow(disabledBySABRStreamingUrlString) {
|
||||
opcode == Opcode.SPUT_OBJECT &&
|
||||
getReference<FieldReference>()?.type == mediaFetchEnumClass
|
||||
getReference<FieldReference>()?.type == mediaFetchEnumClass
|
||||
}
|
||||
|
||||
Pair(
|
||||
@@ -280,17 +284,15 @@ internal fun spoofVideoStreamsPatch(
|
||||
)
|
||||
}
|
||||
|
||||
val sabrFingerprint = fingerprint {
|
||||
val sabrMethod = firstMutableMethodDeclaratively {
|
||||
returnType(mediaFetchEnumClass)
|
||||
opcodes(
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.RETURN_OBJECT,
|
||||
)
|
||||
custom { method, _ ->
|
||||
!method.parameterTypes.isEmpty()
|
||||
}
|
||||
custom { parameterTypes.isEmpty() }
|
||||
}
|
||||
sabrFingerprint.method.addInstructionsWithLabels(
|
||||
sabrMethod.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableSABR()Z
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package app.revanced.patches.solidexplorer2.functionality.filesize
|
||||
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.onReadyMethod by gettingFirstMutableMethodDeclaratively {
|
||||
internal val onReadyMethodMatch = firstMethodComposite {
|
||||
name("onReady")
|
||||
definingClass("Lpl/solidexplorer/plugins/texteditor/TextEditor;")
|
||||
opcodes(
|
||||
|
||||
@@ -12,11 +12,11 @@ val `Remove file size limit` by creatingBytecodePatch(
|
||||
compatibleWith("pl.solidexplorer2")
|
||||
|
||||
apply {
|
||||
onReadyMethod.apply {
|
||||
val cmpIndex = onReadyMethod.indices.first() + 1 // TODO
|
||||
val cmpResultRegister = getInstruction<ThreeRegisterInstruction>(cmpIndex).registerA
|
||||
onReadyMethodMatch.let {
|
||||
val cmpIndex = it.indices.first() + 1
|
||||
val cmpResultRegister = it.method.getInstruction<ThreeRegisterInstruction>(cmpIndex).registerA
|
||||
|
||||
replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0")
|
||||
it.method.replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.booleanOption
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
@@ -22,7 +23,7 @@ private val customThemeBytecodePatch = bytecodePatch {
|
||||
val colorSpaceUtilsClassDef = colorSpaceUtilsClassMethod.immutableClassDef
|
||||
|
||||
// Hook a util method that converts ARGB to RGBA in the sRGB color space to replace hardcoded accent colors.
|
||||
convertArgbToRgbaMethod.match(colorSpaceUtilsClassDef).method.apply {
|
||||
colorSpaceUtilsClassDef.getConvertArgbToRgbaMethod().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
@@ -40,7 +41,7 @@ private val customThemeBytecodePatch = bytecodePatch {
|
||||
val invokeParseColorIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Landroid/graphics/Color;" &&
|
||||
reference.name == "parseColor"
|
||||
reference.name == "parseColor"
|
||||
}
|
||||
val parsedColorRegister = getInstruction<OneRegisterInstruction>(invokeParseColorIndex + 1).registerA
|
||||
|
||||
@@ -61,7 +62,7 @@ private val customThemeBytecodePatch = bytecodePatch {
|
||||
val invokeArgbIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Landroid/graphics/Color;" &&
|
||||
reference.name == "argb"
|
||||
reference.name == "argb"
|
||||
}
|
||||
val argbColorRegister = getInstruction<OneRegisterInstruction>(invokeArgbIndex + 1).registerA
|
||||
|
||||
@@ -97,7 +98,7 @@ val customThemePatch = resourcePatch(
|
||||
default = false,
|
||||
name = "Override player gradient color",
|
||||
description =
|
||||
"Apply primary background color to the player gradient color, which changes dynamically with the song.",
|
||||
"Apply primary background color to the player gradient color, which changes dynamically with the song.",
|
||||
required = false,
|
||||
)
|
||||
|
||||
@@ -105,7 +106,7 @@ val customThemePatch = resourcePatch(
|
||||
default = "#FF121212",
|
||||
name = "Secondary background color",
|
||||
description = "The secondary background color. (e.g. playlist list in home, player artist, song credits). " +
|
||||
"Can be a hex color or a resource reference.\",",
|
||||
"Can be a hex color or a resource reference.\",",
|
||||
required = true,
|
||||
)
|
||||
|
||||
@@ -120,7 +121,7 @@ val customThemePatch = resourcePatch(
|
||||
default = "#FF1ABC54",
|
||||
name = "Pressed accent color",
|
||||
description = "The color when accented buttons are pressed, by default slightly darker than accent. " +
|
||||
"Can be a hex color or a resource reference.",
|
||||
"Can be a hex color or a resource reference.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
@@ -143,38 +144,38 @@ val customThemePatch = resourcePatch(
|
||||
node.textContent = when (name) {
|
||||
// Main background color.
|
||||
"gray_7",
|
||||
// Left sidebar background color in tablet mode.
|
||||
// Left sidebar background color in tablet mode.
|
||||
"gray_10",
|
||||
// Gradient next to user photo and "All" in home page.
|
||||
// Gradient next to user photo and "All" in home page.
|
||||
"dark_base_background_base",
|
||||
// "Add account", "Settings and privacy", "View Profile" left sidebar background color.
|
||||
// "Add account", "Settings and privacy", "View Profile" left sidebar background color.
|
||||
"dark_base_background_elevated_base",
|
||||
// Song/player gradient start/end color.
|
||||
// Song/player gradient start/end color.
|
||||
"bg_gradient_start_color", "bg_gradient_end_color",
|
||||
// Login screen background color and gradient start.
|
||||
// Login screen background color and gradient start.
|
||||
"sthlm_blk", "sthlm_blk_grad_start",
|
||||
// Misc.
|
||||
// Misc.
|
||||
"image_placeholder_color",
|
||||
-> backgroundColor
|
||||
-> backgroundColor
|
||||
|
||||
// "About the artist" background color in song player.
|
||||
"gray_15",
|
||||
// Track credits, merch background color in song player.
|
||||
// Track credits, merch background color in song player.
|
||||
"track_credits_card_bg", "benefit_list_default_color", "merch_card_background",
|
||||
// Playlist list background in home page.
|
||||
// Playlist list background in home page.
|
||||
"opacity_white_10",
|
||||
// "What's New" pills background.
|
||||
// "What's New" pills background.
|
||||
"dark_base_background_tinted_highlight",
|
||||
-> backgroundColorSecondary
|
||||
-> backgroundColorSecondary
|
||||
|
||||
"dark_brightaccent_background_base",
|
||||
"dark_base_text_brightaccent",
|
||||
"green_light",
|
||||
"spotify_green_157",
|
||||
-> accentColor
|
||||
-> accentColor
|
||||
|
||||
"dark_brightaccent_background_press",
|
||||
-> accentColorPressed
|
||||
-> accentColorPressed
|
||||
|
||||
else -> continue
|
||||
}
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
package app.revanced.patches.spotify.layout.theme
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.firstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMethod
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMutableMethod
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.patcher.unorderedAllOf
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.colorSpaceUtilsClassMethod by gettingFirstMethodDeclaratively {
|
||||
strings("The specified color must be encoded in an RGB color space.") // Partial string match.
|
||||
instructions("The specified color must be encoded in an RGB color space."(String::contains))
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.convertArgbToRgbaMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getConvertArgbToRgbaMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL)
|
||||
returnType("J")
|
||||
parameterTypes("J")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.parseLottieJsonMethod by gettingFirstMethodDeclaratively {
|
||||
strings("Unsupported matte type: ")
|
||||
}
|
||||
internal val BytecodePatchContext.parseLottieJsonMethod by gettingFirstMutableMethod("Unsupported matte type: ")
|
||||
|
||||
internal val BytecodePatchContext.parseAnimatedColorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.parseAnimatedColorMethod by gettingFirstMutableMethodDeclaratively {
|
||||
parameterTypes("L", "F")
|
||||
returnType("Ljava/lang/Object;")
|
||||
custom { method, _ ->
|
||||
method.containsLiteralInstruction(255.0) &&
|
||||
method.containsLiteralInstruction(1.0)
|
||||
}
|
||||
instructions(predicates = unorderedAllOf(255.0.toRawBits()(), 1.0.toRawBits()()))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package app.revanced.patches.spotify.misc.extension
|
||||
|
||||
internal val BytecodePatchContext.loadOrbitLibraryMethod by gettingFirstMethodDeclaratively {
|
||||
strings("orbit_library_load", "orbit-jni-spotify")
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
|
||||
internal val loadOrbitLibraryMethodMatch = firstMethodComposite {
|
||||
instructions(
|
||||
"orbit_library_load"(),
|
||||
"orbit-jni-spotify"()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ internal val loadOrbitLibraryHook = extensionHook {
|
||||
// FIXME: Creating this is a mess and needs refactoring.
|
||||
extensionHook(
|
||||
getInsertIndex = {
|
||||
loadOrbitLibraryMethod.stringMatches.last().index
|
||||
loadOrbitLibraryMethodMatch.stringMatches.last().index
|
||||
},
|
||||
getContextRegister = { method ->
|
||||
val contextReferenceIndex = method.indexOfFirstInstruction {
|
||||
@@ -25,6 +25,6 @@ internal val loadOrbitLibraryHook = extensionHook {
|
||||
|
||||
"v$contextRegister"
|
||||
},
|
||||
fingerprint = loadOrbitLibraryMethod,
|
||||
fingerprint = loadOrbitLibraryMethodMatch,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package app.revanced.patches.spotify.misc.fix.login
|
||||
|
||||
import app.revanced.patcher.firstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMethod
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMutableMethod
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.literal
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.katanaProxyLoginMethodHandlerClassMethod by gettingFirstMethodDeclaratively {
|
||||
strings("katana_proxy_auth")
|
||||
}
|
||||
internal val BytecodePatchContext.katanaProxyLoginMethodHandlerClassMethod by gettingFirstMethod("katana_proxy_auth")
|
||||
|
||||
internal val BytecodePatchContext.katanaProxyLoginMethodTryAuthorizeMethod by gettingFirstMethodDeclaratively {
|
||||
strings("e2e")
|
||||
literal { 0 }
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getKatanaProxyLoginMethodTryAuthorizeMethod() = firstMutableMethodDeclaratively("e2e") {
|
||||
instructions(0L())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package app.revanced.patches.spotify.misc.fix.login
|
||||
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused", "ObjectPropertyName")
|
||||
val `Fix Facebook login` by creatingBytecodePatch(
|
||||
description =
|
||||
"Fix logging in with Facebook when the app is patched by always opening the login in a web browser window.",
|
||||
"Fix logging in with Facebook when the app is patched by always opening the login in a web browser window.",
|
||||
) {
|
||||
compatibleWith("com.spotify.music")
|
||||
|
||||
@@ -16,13 +17,9 @@ val `Fix Facebook login` by creatingBytecodePatch(
|
||||
// which ends up making the Facebook server reject with an invalid key hash for the app signature.
|
||||
// Override the Facebook SDK to always handle the login using the web browser, which does not perform
|
||||
// signature checks.
|
||||
|
||||
val katanaProxyLoginMethodHandlerClass = katanaProxyLoginMethodHandlerClassMethod.immutableClassDef
|
||||
// Always return 0 (no Intent was launched) as the result of trying to authorize with the Facebook app to
|
||||
// make the login fallback to a web browser window.
|
||||
katanaProxyLoginMethodTryAuthorizeMethod
|
||||
.match(katanaProxyLoginMethodHandlerClass)
|
||||
.method
|
||||
.returnEarly(0)
|
||||
katanaProxyLoginMethodHandlerClassMethod.immutableClassDef
|
||||
.getKatanaProxyLoginMethodTryAuthorizeMethod().returnEarly(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
package app.revanced.patches.spotify.misc.lyrics
|
||||
|
||||
import app.revanced.patcher.classDef
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.firstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.method
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
@@ -59,8 +65,6 @@ val `Change lyrics provider` by creatingBytecodePatch(
|
||||
}
|
||||
|
||||
apply {
|
||||
val httpClientBuilderMethod = httpClientBuilderMethod.immutableMethod
|
||||
|
||||
// region Create a modified copy of the HTTP client builder method with the custom lyrics provider host.
|
||||
|
||||
val patchedHttpClientBuilderMethod = with(httpClientBuilderMethod) {
|
||||
@@ -70,7 +74,7 @@ val `Change lyrics provider` by creatingBytecodePatch(
|
||||
val setUrlBuilderHostIndex = indexOfFirstInstructionReversedOrThrow(invokeBuildUrlIndex) {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Lokhttp3/HttpUrl${"$"}Builder;" &&
|
||||
reference.parameterTypes.firstOrNull() == "Ljava/lang/String;"
|
||||
reference.parameterTypes.firstOrNull() == "Ljava/lang/String;"
|
||||
}
|
||||
val hostRegister = getInstruction<FiveRegisterInstruction>(setUrlBuilderHostIndex).registerD
|
||||
|
||||
@@ -90,17 +94,13 @@ val `Change lyrics provider` by creatingBytecodePatch(
|
||||
|
||||
// region Replace the call to the HTTP client builder method used exclusively for lyrics by the modified one.
|
||||
|
||||
val getLyricsHttpClientFingerprint = fingerprint {
|
||||
val getLyricsHttpClientMethod = firstMutableMethodDeclaratively {
|
||||
returnType(httpClientBuilderMethod.returnType)
|
||||
parameterTypes()
|
||||
custom { method, _ ->
|
||||
method.indexOfFirstInstruction {
|
||||
getReference<MethodReference>() == httpClientBuilderMethod
|
||||
} >= 0
|
||||
}
|
||||
instructions(method { this == httpClientBuilderMethod })
|
||||
}
|
||||
|
||||
getLyricsHttpClientFingerprint.method.apply {
|
||||
getLyricsHttpClientMethod.apply {
|
||||
val getLyricsHttpClientIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>() == httpClientBuilderMethod
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.spotify.misc.lyrics
|
||||
|
||||
import app.revanced.patcher.gettingFirstMethod
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
@@ -8,16 +9,4 @@ import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val BytecodePatchContext.httpClientBuilderMethod by gettingFirstMethodDeclaratively {
|
||||
strings("client == null", "scheduler == null")
|
||||
}
|
||||
|
||||
internal fun getLyricsHttpClientFingerprint(httpClientBuilderMethodReference: MethodReference) = fingerprint {
|
||||
returnType(httpClientBuilderMethodReference.returnType)
|
||||
parameterTypes()
|
||||
custom { method, _ ->
|
||||
method.indexOfFirstInstruction {
|
||||
getReference<MethodReference>() == httpClientBuilderMethodReference
|
||||
} >= 0
|
||||
}
|
||||
}
|
||||
internal val BytecodePatchContext.httpClientBuilderMethod by gettingFirstMethod("client == null", "scheduler == null")
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package app.revanced.patches.tumblr.featureflags
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
@@ -18,7 +17,7 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
// Some features seem to be very old and never removed, though, such as Google Login.
|
||||
// The startIndex of the opcode pattern is at the start of the function after the arg null check.
|
||||
// we want to insert our instructions there.
|
||||
internal val BytecodePatchContext.getFeatureValueMethod by gettingFirstMethodDeclaratively {
|
||||
internal val getFeatureValueMethodMatch = firstMethodComposite("feature") {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Ljava/lang/String;")
|
||||
parameterTypes("L", "Z")
|
||||
@@ -27,5 +26,4 @@ internal val BytecodePatchContext.getFeatureValueMethod by gettingFirstMethodDec
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT,
|
||||
)
|
||||
strings("feature")
|
||||
}
|
||||
|
||||
@@ -24,54 +24,55 @@ val overrideFeatureFlagsPatch = bytecodePatch(
|
||||
) {
|
||||
|
||||
apply {
|
||||
val configurationClass = getFeatureValueMethod.immutableMethod.definingClass
|
||||
val featureClass = getFeatureValueMethod.immutableMethod.parameterTypes[0].toString()
|
||||
getFeatureValueMethodMatch.let { match ->
|
||||
val configurationClass = match.immutableMethod.definingClass
|
||||
val featureClass = match.immutableMethod.parameterTypes[0].toString()
|
||||
|
||||
// The method we want to inject into does not have enough registers, so we inject a helper method
|
||||
// and inject more instructions into it later, see addOverride.
|
||||
// This is not in an extension since the unused variable would get compiled away and the method would
|
||||
// get compiled to only have one register, which is not enough for our later injected instructions.
|
||||
val helperMethod = ImmutableMethod(
|
||||
getFeatureValueMethod.immutableMethod.definingClass,
|
||||
"getValueOverride",
|
||||
listOf(ImmutableMethodParameter(featureClass, null, "feature")),
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(4),
|
||||
).toMutable().apply {
|
||||
// This is the equivalent of
|
||||
// String featureName = feature.toString()
|
||||
// <inject more instructions here later>
|
||||
// return null
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
# toString() the enum value
|
||||
invoke-virtual {p1}, $featureClass->toString()Ljava/lang/String;
|
||||
move-result-object v0
|
||||
|
||||
# !!! If you add more instructions above this line, update helperInsertIndex below!
|
||||
# Here we will insert one compare & return for every registered Feature override
|
||||
# This is done below in the addOverride function
|
||||
|
||||
# If none of the overrides returned a value, we should return null
|
||||
const/4 v0, 0x0
|
||||
return-object v0
|
||||
""",
|
||||
)
|
||||
}.also { helperMethod ->
|
||||
getFeatureValueMethod.classDef.methods.add(helperMethod)
|
||||
}
|
||||
// The method we want to inject into does not have enough registers, so we inject a helper method
|
||||
// and inject more instructions into it later, see addOverride.
|
||||
// This is not in an extension since the unused variable would get compiled away and the method would
|
||||
// get compiled to only have one register, which is not enough for our later injected instructions.
|
||||
val helperMethod = ImmutableMethod(
|
||||
match.immutableMethod.definingClass,
|
||||
"getValueOverride",
|
||||
listOf(ImmutableMethodParameter(featureClass, null, "feature")),
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(4),
|
||||
).toMutable().apply {
|
||||
// This is the equivalent of
|
||||
// String featureName = feature.toString()
|
||||
// <inject more instructions here later>
|
||||
// return null
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
# toString() the enum value
|
||||
invoke-virtual {p1}, $featureClass->toString()Ljava/lang/String;
|
||||
move-result-object v0
|
||||
|
||||
# !!! If you add more instructions above this line, update helperInsertIndex below!
|
||||
# Here we will insert one compare & return for every registered Feature override
|
||||
# This is done below in the addOverride function
|
||||
|
||||
# If none of the overrides returned a value, we should return null
|
||||
const/4 v0, 0x0
|
||||
return-object v0
|
||||
""",
|
||||
)
|
||||
}.also { helperMethod ->
|
||||
match.classDef.methods.add(helperMethod)
|
||||
}
|
||||
|
||||
// Here we actually insert the hook to call our helper method and return its value if it returns not null
|
||||
// This is equivalent to
|
||||
// String forcedValue = getValueOverride(feature)
|
||||
// if (forcedValue != null) return forcedValue
|
||||
val getFeatureIndex = getFeatureValueMethod.indices.first()
|
||||
getFeatureValueMethod.addInstructionsWithLabels(
|
||||
getFeatureIndex,
|
||||
// Here we actually insert the hook to call our helper method and return its value if it returns not null
|
||||
// This is equivalent to
|
||||
// String forcedValue = getValueOverride(feature)
|
||||
// if (forcedValue != null) return forcedValue
|
||||
val getFeatureIndex = match.indices.first()
|
||||
match.method.addInstructionsWithLabels(
|
||||
getFeatureIndex,
|
||||
"""
|
||||
# Call the Helper Method with the Feature
|
||||
invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String;
|
||||
@@ -85,31 +86,32 @@ val overrideFeatureFlagsPatch = bytecodePatch(
|
||||
:is_null
|
||||
nop
|
||||
""",
|
||||
)
|
||||
|
||||
val helperInsertIndex = 2
|
||||
addFeatureFlagOverride = { name, value ->
|
||||
// For every added override, we add a few instructions in the middle of the helper method
|
||||
// to check if the feature is the one we want and return the override value if it is.
|
||||
// This is equivalent to
|
||||
// if (featureName == {name}) return {value}
|
||||
helperMethod.addInstructionsWithLabels(
|
||||
helperInsertIndex,
|
||||
"""
|
||||
# v0 is still the string name of the currently checked feature from above
|
||||
# Compare the current string with the override string
|
||||
const-string v1, "$name"
|
||||
invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
move-result v1
|
||||
# If the current string is the one we want to override, we return the override value
|
||||
if-eqz v1, :no_override
|
||||
const-string v1, "$value"
|
||||
return-object v1
|
||||
# Else we just continue...
|
||||
:no_override
|
||||
nop
|
||||
""",
|
||||
)
|
||||
|
||||
val helperInsertIndex = 2
|
||||
addFeatureFlagOverride = { name, value ->
|
||||
// For every added override, we add a few instructions in the middle of the helper method
|
||||
// to check if the feature is the one we want and return the override value if it is.
|
||||
// This is equivalent to
|
||||
// if (featureName == {name}) return {value}
|
||||
helperMethod.addInstructionsWithLabels(
|
||||
helperInsertIndex,
|
||||
"""
|
||||
# v0 is still the string name of the currently checked feature from above
|
||||
# Compare the current string with the override string
|
||||
const-string v1, "$name"
|
||||
invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
move-result v1
|
||||
# If the current string is the one we want to override, we return the override value
|
||||
if-eqz v1, :no_override
|
||||
const-string v1, "$value"
|
||||
return-object v1
|
||||
# Else we just continue...
|
||||
:no_override
|
||||
nop
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
package app.revanced.patches.tumblr.fixes
|
||||
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
// Fingerprint for the addQueryParam method from retrofit2
|
||||
// https://github.com/square/retrofit/blob/trunk/retrofit/src/main/java/retrofit2/RequestBuilder.java#L186
|
||||
// Injecting here allows modifying dynamically set query parameters
|
||||
internal val BytecodePatchContext.addQueryParamMethod by gettingFirstMethodDeclaratively {
|
||||
// Fingerprint for the addQueryParam method from retrofit2:
|
||||
// https://github.com/square/retrofit/blob/trunk/retrofit/src/main/java/retrofit2/RequestBuilder.java#L186.
|
||||
// Injecting here allows modifying dynamically set query parameters.
|
||||
internal val BytecodePatchContext.addQueryParamMethod by gettingFirstMutableMethodDeclaratively("Malformed URL. Base: ", ", Relative: ") {
|
||||
parameterTypes("Ljava/lang/String;", "Ljava/lang/String;", "Z")
|
||||
strings("Malformed URL. Base: ", ", Relative: ")
|
||||
}
|
||||
|
||||
// Fingerprint for the parseHttpMethodAndPath method from retrofit2
|
||||
// Fingerprint for the parseHttpMethodAndPath method from retrofit2:
|
||||
// https://github.com/square/retrofit/blob/ebf87b10997e2136af4d335276fa950221852c64/retrofit/src/main/java/retrofit2/RequestFactory.java#L270-L302
|
||||
// Injecting here allows modifying the path/query params of API endpoints defined via annotations
|
||||
internal val BytecodePatchContext.httpPathParserMethod by gettingFirstMethodDeclaratively {
|
||||
// Injecting here allows modifying the path/query params of API endpoints defined via annotations.
|
||||
internal val httpPathParserMethodMatch = firstMethodComposite("Only one HTTP method is allowed. Found: %s and %s.") {
|
||||
opcodes(
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
)
|
||||
strings("Only one HTTP method is allowed. Found: %s and %s.")
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ val `Fix old versions` by creatingBytecodePatch(
|
||||
|
||||
// Remove the live query parameters from the path when it's specified via a @METHOD annotation.
|
||||
for (liveQueryParameter in liveQueryParameters) {
|
||||
httpPathParserMethod.addInstructions(
|
||||
httpPathParserMethod.indices.last() + 1,
|
||||
httpPathParserMethodMatch.method.addInstructions(
|
||||
httpPathParserMethodMatch.indices.last() + 1,
|
||||
"""
|
||||
# urlPath = urlPath.replace(liveQueryParameter, "")
|
||||
const-string p1, "$liveQueryParameter"
|
||||
|
||||
@@ -24,9 +24,9 @@ val filterTimelineObjectsPatch = bytecodePatch(
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
apply {
|
||||
val filterInsertIndex = timelineFilterExtensionMethod.indices.first()
|
||||
val filterInsertIndex = timelineFilterExtensionMethodMatch.indices.first()
|
||||
|
||||
timelineFilterExtensionMethod.apply {
|
||||
timelineFilterExtensionMethodMatch.method.apply {
|
||||
val addInstruction = getInstruction<BuilderInstruction35c>(filterInsertIndex + 1)
|
||||
|
||||
val filterListRegister = addInstruction.registerC
|
||||
@@ -50,8 +50,8 @@ val filterTimelineObjectsPatch = bytecodePatch(
|
||||
arrayOf(
|
||||
timelineConstructorMethod to 1,
|
||||
postsResponseConstructorMethod to 2,
|
||||
).forEach { (fingerprint, timelineObjectsRegister) ->
|
||||
fingerprint.method.addInstructions(
|
||||
).forEach { (method, timelineObjectsRegister) ->
|
||||
method.addInstructions(
|
||||
0,
|
||||
"invoke-static {p$timelineObjectsRegister}, " +
|
||||
"Lapp/revanced/extension/tumblr/patches/TimelineFilterPatch;->" +
|
||||
|
||||
@@ -1,39 +1,33 @@
|
||||
package app.revanced.patches.tumblr.timelinefilter
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
// This is the constructor of the PostsResponse class.
|
||||
// The same applies here as with the TimelineConstructorMethod.
|
||||
internal val BytecodePatchContext.postsResponseConstructorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.postsResponseConstructorMethod by gettingFirstMutableMethodDeclaratively {
|
||||
definingClass { endsWith("/PostsResponse;") }
|
||||
accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PUBLIC)
|
||||
custom { method, classDef -> classDef.endsWith("/PostsResponse;") && method.parameters.size == 4 }
|
||||
custom { parameters.size == 4 }
|
||||
}
|
||||
|
||||
// This is the constructor of the Timeline class.
|
||||
// It receives the List<TimelineObject> as an argument with a @Json annotation, so this should be the first time
|
||||
// that the List<TimelineObject> is exposed in non-library code.
|
||||
internal val BytecodePatchContext.timelineConstructorMethod by gettingFirstMethodDeclaratively {
|
||||
strings("timelineObjectsList")
|
||||
custom { method, classDef ->
|
||||
classDef.endsWith("/Timeline;") && method.parameters[0].type == "Ljava/util/List;"
|
||||
}
|
||||
internal val BytecodePatchContext.timelineConstructorMethod by gettingFirstMutableMethodDeclaratively("timelineObjectsList") {
|
||||
definingClass { endsWith("/Timeline;") }
|
||||
custom { parameters[0].type == "Ljava/util/List;" }
|
||||
}
|
||||
|
||||
// This fingerprints the extension TimelineFilterPatch.filterTimeline method.
|
||||
// The opcode fingerprint is searching for
|
||||
// if ("BLOCKED_OBJECT_DUMMY".equals(elementType)) iterator.remove();
|
||||
internal val BytecodePatchContext.timelineFilterExtensionMethod by gettingFirstMethodDeclaratively {
|
||||
internal val timelineFilterExtensionMethodMatch = firstMethodComposite("BLOCKED_OBJECT_DUMMY") {
|
||||
definingClass { endsWith("/TimelineFilterPatch;") }
|
||||
opcodes(
|
||||
Opcode.CONST_STRING, // "BLOCKED_OBJECT_DUMMY"
|
||||
Opcode.INVOKE_VIRTUAL, // HashSet.add(^)
|
||||
)
|
||||
strings("BLOCKED_OBJECT_DUMMY")
|
||||
custom { _, classDef ->
|
||||
classDef.endsWith("/TimelineFilterPatch;")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
package app.revanced.patches.twitter.interaction.downloads
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.buildMediaOptionsSheetMethod by gettingFirstMethodDeclaratively {
|
||||
internal val buildMediaOptionsSheetMethodMatch = firstMethodComposite("mediaEntity", "media_options_sheet") {
|
||||
opcodes(
|
||||
Opcode.IF_EQ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.GOTO_16,
|
||||
Opcode.NEW_INSTANCE,
|
||||
)
|
||||
strings("mediaEntity", "media_options_sheet")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.constructMediaOptionsSheetMethod by gettingFirstMethodDeclaratively {
|
||||
internal val constructMediaOptionsSheetMethodMatch = firstMethodComposite("captionsState") {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
strings("captionsState")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.showDownloadVideoUpsellBottomSheetMethod by gettingFirstMethodDeclaratively {
|
||||
internal val showDownloadVideoUpsellBottomSheetMethodMatch = firstMethodComposite("mediaEntity", "url") {
|
||||
returnType("Z")
|
||||
strings("mediaEntity", "url")
|
||||
opcodes(Opcode.IF_EQZ)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.twitter.interaction.downloads
|
||||
|
||||
import app.revanced.patcher.MatchBuilder
|
||||
import app.revanced.patcher.extensions.*
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
@@ -13,13 +14,13 @@ val `Unlock downloads` by creatingBytecodePatch(
|
||||
compatibleWith("com.twitter.android")
|
||||
|
||||
apply {
|
||||
fun Fingerprint.patch(getRegisterAndIndex: Fingerprint.() -> Pair<Int, Int>) {
|
||||
fun MatchBuilder.patch(getRegisterAndIndex: MatchBuilder.() -> Pair<Int, Int>) {
|
||||
val (index, register) = getRegisterAndIndex()
|
||||
method.addInstruction(index, "const/4 v$register, 0x1")
|
||||
}
|
||||
|
||||
// Allow downloads for non-premium users.
|
||||
showDownloadVideoUpsellBottomSheetMethod.patch {
|
||||
showDownloadVideoUpsellBottomSheetMethodMatch.patch {
|
||||
val checkIndex = indices.first()
|
||||
val register = method.getInstruction<OneRegisterInstruction>(checkIndex).registerA
|
||||
|
||||
@@ -27,7 +28,7 @@ val `Unlock downloads` by creatingBytecodePatch(
|
||||
}
|
||||
|
||||
// Force show the download menu item.
|
||||
constructMediaOptionsSheetMethod.patch {
|
||||
constructMediaOptionsSheetMethodMatch.patch {
|
||||
val showDownloadButtonIndex = method.instructions.lastIndex - 1
|
||||
val register = method.getInstruction<TwoRegisterInstruction>(showDownloadButtonIndex).registerA
|
||||
|
||||
@@ -35,7 +36,7 @@ val `Unlock downloads` by creatingBytecodePatch(
|
||||
}
|
||||
|
||||
// Make GIFs downloadable.
|
||||
buildMediaOptionsSheetMethod.let {
|
||||
buildMediaOptionsSheetMethodMatch.let {
|
||||
it.method.apply {
|
||||
val checkMediaTypeIndex = it.indices.first()
|
||||
val checkMediaTypeInstruction = getInstruction<TwoRegisterInstruction>(checkMediaTypeIndex)
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package app.revanced.patches.youtube.ad.getpremium
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patcher.*
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.getPremiumViewMethod by gettingFirstMethodDeclaratively {
|
||||
internal val getPremiumViewMethodMatch = firstMethodComposite {
|
||||
name("onMeasure")
|
||||
definingClass("Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;")
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("I", "I")
|
||||
@@ -19,8 +16,4 @@ internal val BytecodePatchContext.getPremiumViewMethod by gettingFirstMethodDecl
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID,
|
||||
)
|
||||
custom { method, _ ->
|
||||
method.name == "onMeasure" &&
|
||||
method.definingClass == "Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,15 @@ val hideGetPremiumPatch = bytecodePatch(
|
||||
SwitchPreference("revanced_hide_get_premium"),
|
||||
)
|
||||
|
||||
getPremiumViewMethod.apply {
|
||||
val startIndex = getPremiumViewMethod.indices.first()
|
||||
val measuredWidthRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val measuredHeightInstruction = getInstruction<TwoRegisterInstruction>(startIndex + 1)
|
||||
getPremiumViewMethodMatch.let {
|
||||
val startIndex = it.indices.first()
|
||||
val measuredWidthRegister = it.method.getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val measuredHeightInstruction = it.method.getInstruction<TwoRegisterInstruction>(startIndex + 1)
|
||||
|
||||
val measuredHeightRegister = measuredHeightInstruction.registerA
|
||||
val tempRegister = measuredHeightInstruction.registerB
|
||||
|
||||
addInstructionsWithLabels(
|
||||
it.method.addInstructionsWithLabels(
|
||||
startIndex + 2,
|
||||
"""
|
||||
# Override the internal measurement of the layout with zero values.
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
package app.revanced.patches.youtube.interaction.dialog
|
||||
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patcher.*
|
||||
|
||||
internal val BytecodePatchContext.createDialogMethod by gettingFirstMethodDeclaratively {
|
||||
internal val createDialogMethodMatch = firstMethodComposite {
|
||||
returnType("V")
|
||||
parameterTypes("L", "L", "Ljava/lang/String;")
|
||||
instructions(
|
||||
methodCall(smali = "Landroid/app/AlertDialog\$Builder;->setNegativeButton(ILandroid/content/DialogInterface\$OnClickListener;)Landroid/app/AlertDialog\$Builder;"),
|
||||
methodCall(smali = "Landroid/app/AlertDialog\$Builder;->setOnCancelListener(Landroid/content/DialogInterface\$OnCancelListener;)Landroid/app/AlertDialog\$Builder;"),
|
||||
methodCall(smali = "Landroid/app/AlertDialog\$Builder;->create()Landroid/app/AlertDialog;"),
|
||||
methodCall(smali = "Landroid/app/AlertDialog;->show()V"),
|
||||
method { toString() == $$"Landroid/app/AlertDialog$Builder;->setNegativeButton(ILandroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;" },
|
||||
method { toString() == $$"Landroid/app/AlertDialog$Builder;->setOnCancelListener(Landroid/content/DialogInterface$OnCancelListener;)Landroid/app/AlertDialog$Builder;" },
|
||||
method { toString() == $$"Landroid/app/AlertDialog$Builder;->create()Landroid/app/AlertDialog;" },
|
||||
method { toString() == "Landroid/app/AlertDialog;->show()V" },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ val `Remove viewer discretion dialog` by creatingBytecodePatch(
|
||||
SwitchPreference("revanced_remove_viewer_discretion_dialog"),
|
||||
)
|
||||
|
||||
createDialogMethod.let {
|
||||
createDialogMethodMatch.let {
|
||||
it.method.apply {
|
||||
val showDialogIndex = it.indices.last() // TODO
|
||||
val showDialogIndex = it.indices.last()
|
||||
val dialogRegister = getInstruction<FiveRegisterInstruction>(showDialogIndex).registerC
|
||||
|
||||
replaceInstructions(
|
||||
|
||||
@@ -34,7 +34,7 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
// Find the required methods to tap the seekbar.
|
||||
val seekbarTappingMethods = onTouchEventHandlerMethod.let {
|
||||
val seekbarTappingMethods = onTouchEventHandlerMethodMatch.let {
|
||||
fun getReference(index: Int) = it.method.getInstruction<ReferenceInstruction>(index)
|
||||
.reference as MethodReference
|
||||
|
||||
@@ -44,7 +44,7 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
|
||||
seekbarTappingMethod.let {
|
||||
seekbarTappingMethodMatch.let {
|
||||
val insertIndex = it.indices.last() + 1
|
||||
|
||||
it.method.apply {
|
||||
|
||||
@@ -2,6 +2,7 @@ package app.revanced.patches.youtube.interaction.seekbar
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -42,16 +43,16 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
|
||||
// Restore the behaviour to slide to seek.
|
||||
|
||||
val checkIndex = slideToSeekMethod.indices.first()
|
||||
val checkReference = slideToSeekMethod.getInstruction(checkIndex)
|
||||
val checkIndex = slideToSeekMethodMatch.indices.first()
|
||||
val checkReference = slideToSeekMethodMatch.method.getInstruction(checkIndex)
|
||||
.getReference<MethodReference>()!!
|
||||
|
||||
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->isSlideToSeekDisabled(Z)Z"
|
||||
|
||||
// A/B check method was only called on this class.
|
||||
slideToSeekMethod.classDef.methods.forEach { method ->
|
||||
slideToSeekMethodMatch.classDef.methods.forEach { method ->
|
||||
method.findInstructionIndicesReversed {
|
||||
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>() == checkReference
|
||||
opcode == Opcode.INVOKE_VIRTUAL && methodReference == checkReference
|
||||
}.forEach { index ->
|
||||
method.apply {
|
||||
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
@@ -73,7 +74,7 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
|
||||
// Disable the double speed seek gesture.
|
||||
if (is_19_17_or_greater) {
|
||||
disableFastForwardGestureMethod.let {
|
||||
disableFastForwardGestureMethodMatch.let {
|
||||
it.method.apply {
|
||||
val targetIndex = it.indices.last()
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
@@ -88,7 +89,7 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
disableFastForwardLegacyMethod.let {
|
||||
disableFastForwardLegacyMethodMatch.let {
|
||||
it.method.apply {
|
||||
val insertIndex = it.indices.last() + 1
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
@@ -40,7 +40,7 @@ internal fun ClassDef.getAllowSwipingUpGestureMethod() = firstMutableMethodDecla
|
||||
parameterTypes("L")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.disableFastForwardLegacyMethod by gettingFirstMethodDeclaratively {
|
||||
internal val disableFastForwardLegacyMethodMatch = firstMethodComposite {
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
opcodes(Opcode.MOVE_RESULT)
|
||||
@@ -48,7 +48,7 @@ internal val BytecodePatchContext.disableFastForwardLegacyMethod by gettingFirst
|
||||
literal { 45411330 }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.disableFastForwardGestureMethod by gettingFirstMethodDeclaratively {
|
||||
internal val disableFastForwardGestureMethodMatch = firstMethodComposite {
|
||||
definingClass { endsWith("/NextGenWatchLayout;") }
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
@@ -80,7 +80,7 @@ internal val customTapAndHoldMethodMatch = firstMethodComposite {
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.onTouchEventHandlerMethod by gettingFirstMethodDeclaratively {
|
||||
internal val onTouchEventHandlerMethodMatch = firstMethodComposite {
|
||||
name("onTouchEvent")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.PUBLIC)
|
||||
returnType("Z")
|
||||
@@ -103,7 +103,7 @@ internal val BytecodePatchContext.onTouchEventHandlerMethod by gettingFirstMetho
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.seekbarTappingMethod by gettingFirstMethodDeclaratively {
|
||||
internal val seekbarTappingMethodMatch = firstMethodComposite {
|
||||
name("onTouchEvent")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
@@ -119,7 +119,7 @@ internal val BytecodePatchContext.seekbarTappingMethod by gettingFirstMethodDecl
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.slideToSeekMethod by gettingFirstMethodDeclaratively {
|
||||
internal val slideToSeekMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("Landroid/view/View;", "F")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package app.revanced.patches.youtube.interaction.swipecontrols
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
@@ -9,14 +11,12 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val BytecodePatchContext.swipeControlsHostActivityMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameterTypes()
|
||||
custom { method, _ ->
|
||||
method.definingClass == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.swipeChangeVideoMethod by gettingFirstMethodDeclaratively {
|
||||
internal val swipeChangeVideoMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
45631116L(), // Swipe to change fullscreen video feature flag.
|
||||
|
||||
@@ -134,9 +134,9 @@ val `Swipe controls` by creatingBytecodePatch(
|
||||
// region patch to enable/disable swipe to change video.
|
||||
|
||||
if (is_19_43_or_greater && !is_20_34_or_greater) {
|
||||
swipeChangeVideoMethod.let {
|
||||
swipeChangeVideoMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it.indices.last(), // TODO
|
||||
it.indices.last(),
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package app.revanced.patches.youtube.layout.hide.endscreencards
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
@@ -14,7 +10,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
|
||||
internal val BytecodePatchContext.layoutCircleMethod by gettingFirstMethodDeclaratively {
|
||||
internal val layoutCircleMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes()
|
||||
returnType("Landroid/view/View;")
|
||||
@@ -28,7 +24,7 @@ internal val BytecodePatchContext.layoutCircleMethod by gettingFirstMethodDeclar
|
||||
literal { layoutCircle }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.layoutIconMethod by gettingFirstMethodDeclaratively {
|
||||
internal val layoutIconMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes()
|
||||
returnType("Landroid/view/View;")
|
||||
@@ -41,7 +37,7 @@ internal val BytecodePatchContext.layoutIconMethod by gettingFirstMethodDeclarat
|
||||
literal { layoutIcon }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.layoutVideoMethod by gettingFirstMethodDeclaratively {
|
||||
internal val layoutVideoMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
parameterTypes()
|
||||
returnType("Landroid/view/View;")
|
||||
@@ -55,16 +51,16 @@ internal val BytecodePatchContext.layoutVideoMethod by gettingFirstMethodDeclara
|
||||
literal { layoutVideo }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.showEndscreenCardsMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.showEndscreenCardsMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
custom { method, classDef ->
|
||||
classDef.methods.count() == 5 &&
|
||||
method.containsLiteralInstruction(0) &&
|
||||
method.containsLiteralInstruction(5) &&
|
||||
method.containsLiteralInstruction(8) &&
|
||||
method.indexOfFirstInstruction {
|
||||
custom {
|
||||
immutableClassDef.methods.count() == 5 &&
|
||||
containsLiteralInstruction(0) &&
|
||||
containsLiteralInstruction(5) &&
|
||||
containsLiteralInstruction(8) &&
|
||||
indexOfFirstInstruction {
|
||||
val reference = getReference<FieldReference>()
|
||||
reference?.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"
|
||||
} >= 0
|
||||
|
||||
@@ -70,12 +70,12 @@ val `Hide end screen cards` by creatingBytecodePatch(
|
||||
|
||||
apply {
|
||||
listOf(
|
||||
layoutCircleMethod,
|
||||
layoutIconMethod,
|
||||
layoutVideoMethod,
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.method.apply {
|
||||
val insertIndex = fingerprint.indices.last() + 1 // TODO
|
||||
layoutCircleMethodMatch,
|
||||
layoutIconMethodMatch,
|
||||
layoutVideoMethodMatch,
|
||||
).forEach { match ->
|
||||
match.method.apply {
|
||||
val insertIndex = match.indices.last() + 1
|
||||
val viewRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||
|
||||
addInstruction(
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
package app.revanced.patches.youtube.layout.hide.endscreensuggestion
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.autoNavConstructorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.autoNavConstructorMethod by gettingFirstMethodDeclaratively("main_app_autonav") {
|
||||
returnType("V")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
strings("main_app_autonav")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.autoNavStatusMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getAutoNavStatusMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.removeOnLayoutChangeListenerMethod by gettingFirstMethodDeclaratively {
|
||||
internal val removeOnLayoutChangeListenerMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
@@ -33,11 +29,10 @@ internal val BytecodePatchContext.removeOnLayoutChangeListenerMethod by gettingF
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
)
|
||||
// This is the only reference present in the entire smali.
|
||||
custom { method, _ ->
|
||||
method.indexOfFirstInstruction {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.name == "removeOnLayoutChangeListener" &&
|
||||
reference.definingClass.endsWith("/YouTubePlayerOverlaysLayout;")
|
||||
} >= 0
|
||||
custom {
|
||||
instructions.anyInstruction {
|
||||
val reference = methodReference
|
||||
reference?.name == "removeOnLayoutChangeListener" && reference.definingClass.endsWith("/YouTubePlayerOverlaysLayout;")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,18 @@ package app.revanced.patches.youtube.layout.hide.endscreensuggestion
|
||||
import app.revanced.patcher.extensions.ExternalLabel
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/HideEndScreenSuggestedVideoPatch;"
|
||||
@@ -44,20 +44,19 @@ val `Hide end screen suggested video` by creatingBytecodePatch(
|
||||
SwitchPreference("revanced_end_screen_suggested_video"),
|
||||
)
|
||||
|
||||
removeOnLayoutChangeListenerMethod.let {
|
||||
removeOnLayoutChangeListenerMethodMatch.let {
|
||||
val endScreenMethod = navigate(it.immutableMethod).to(it.indices.last()).stop() // TODO
|
||||
|
||||
endScreenMethod.apply {
|
||||
val autoNavStatusMethodName = autoNavStatusMethod.match(
|
||||
autoNavConstructorMethod.classDef,
|
||||
).immutableMethod.name
|
||||
val autoNavStatusMethodName = autoNavConstructorMethod.immutableClassDef.getAutoNavStatusMethod().name
|
||||
|
||||
val invokeIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
val reference = methodReference
|
||||
reference?.name == autoNavStatusMethodName &&
|
||||
reference.returnType == "Z" &&
|
||||
reference.parameterTypes.isEmpty()
|
||||
}
|
||||
|
||||
val iGetObjectIndex = indexOfFirstInstructionReversedOrThrow(invokeIndex, Opcode.IGET_OBJECT)
|
||||
val invokeReference = getInstruction<ReferenceInstruction>(invokeIndex).reference
|
||||
val iGetObjectReference = getInstruction<ReferenceInstruction>(iGetObjectIndex).reference
|
||||
|
||||
@@ -1,54 +1,45 @@
|
||||
package app.revanced.patches.youtube.layout.hide.general
|
||||
|
||||
import app.revanced.patcher.StringComparisonType
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.after
|
||||
import app.revanced.patcher.afterAtMost
|
||||
import app.revanced.patcher.checkCast
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutMethod
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
/**
|
||||
* 20.26+
|
||||
*/
|
||||
internal val BytecodePatchContext.hideShowMoreButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val hideShowMoreButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL, AccessFlags.SYNTHETIC)
|
||||
returnType("V")
|
||||
parameterTypes("L", "Ljava/lang/Object;")
|
||||
instructions(
|
||||
ResourceType.LAYOUT("expand_button_down"),
|
||||
methodCall(smali = "Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View;"),
|
||||
method { toString() == "Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View;" },
|
||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.hideShowMoreLegacyButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val hideShowMoreLegacyButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.LAYOUT("expand_button_down"),
|
||||
methodCall(smali = "Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;"),
|
||||
method { toString() == "Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;" },
|
||||
Opcode.MOVE_RESULT_OBJECT(),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.parseElementFromBufferMethod by gettingFirstMethodDeclaratively {
|
||||
internal val parseElementFromBufferMethodMatch = firstMethodComposite {
|
||||
parameterTypes("L", "L", "[B", "L", "L")
|
||||
instructions(
|
||||
Opcode.IGET_OBJECT(),
|
||||
// IGET_BOOLEAN // 20.07+
|
||||
afterAtMost(1, Opcode.INVOKE_INTERFACE()),
|
||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||
|
||||
addString("Failed to parse Element", comparison = StringComparisonType.STARTS_WITH),
|
||||
"Failed to parse Element"(String::startsWith),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -60,7 +51,8 @@ internal val BytecodePatchContext.playerOverlayMethod by gettingFirstMethodDecla
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.showWatermarkMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getShowWatermarkMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L", "L")
|
||||
@@ -69,16 +61,14 @@ internal val BytecodePatchContext.showWatermarkMethod by gettingFirstMethodDecla
|
||||
/**
|
||||
* Matches same method as [wideSearchbarLayoutMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.yoodlesImageViewMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.yoodlesImageViewMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
parameterTypes("L", "L")
|
||||
instructions(
|
||||
ResourceType.ID("youtube_logo"),
|
||||
)
|
||||
instructions(ResourceType.ID("youtube_logo"))
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.crowdfundingBoxMethod by gettingFirstMethodDeclaratively {
|
||||
internal val crowdfundingBoxMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
opcodes(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
@@ -88,7 +78,7 @@ internal val BytecodePatchContext.crowdfundingBoxMethod by gettingFirstMethodDec
|
||||
literal { crowdfundingBoxId }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.albumCardsMethod by gettingFirstMethodDeclaratively {
|
||||
internal val albumCardsMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
opcodes(
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
@@ -101,7 +91,7 @@ internal val BytecodePatchContext.albumCardsMethod by gettingFirstMethodDeclarat
|
||||
literal { albumCardId }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.filterBarHeightMethod by gettingFirstMethodDeclaratively {
|
||||
internal val filterBarHeightMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
opcodes(
|
||||
Opcode.CONST,
|
||||
@@ -112,7 +102,7 @@ internal val BytecodePatchContext.filterBarHeightMethod by gettingFirstMethodDec
|
||||
literal { filterBarHeightId }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.relatedChipCloudMethod by gettingFirstMethodDeclaratively {
|
||||
internal val relatedChipCloudMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
opcodes(
|
||||
Opcode.CONST,
|
||||
@@ -122,7 +112,7 @@ internal val BytecodePatchContext.relatedChipCloudMethod by gettingFirstMethodDe
|
||||
literal { relatedChipCloudMarginId }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.searchResultsChipBarMethod by gettingFirstMethodDeclaratively {
|
||||
internal val searchResultsChipBarMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
opcodes(
|
||||
Opcode.CONST,
|
||||
@@ -134,27 +124,25 @@ internal val BytecodePatchContext.searchResultsChipBarMethod by gettingFirstMeth
|
||||
literal { barContainerHeightId }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.showFloatingMicrophoneButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val showFloatingMicrophoneButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
ResourceType.ID("fab"),
|
||||
checkCast("/FloatingActionButton;", afterAtMost(10)),
|
||||
afterAtMost(10, allOf(Opcode.CHECK_CAST(), "/FloatingActionButton;"())),
|
||||
afterAtMost(15, Opcode.IGET_BOOLEAN()),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.hideViewCountMethod by gettingFirstMethodDeclaratively {
|
||||
internal val hideViewCountMethodMatch = firstMethodComposite(
|
||||
"Has attachmentRuns but drawableRequester is missing.",
|
||||
) {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returnType("Ljava/lang/CharSequence;")
|
||||
|
||||
opcodes(
|
||||
Opcode.RETURN_OBJECT,
|
||||
Opcode.CONST_STRING,
|
||||
Opcode.RETURN_OBJECT,
|
||||
)
|
||||
strings(
|
||||
"Has attachmentRuns but drawableRequester is missing.",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package app.revanced.patches.youtube.layout.hide.general
|
||||
|
||||
import app.revanced.patcher.MatchBuilder
|
||||
import app.revanced.patcher.extensions.*
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -41,30 +43,15 @@ private val hideLayoutComponentsResourcePatch = resourcePatch {
|
||||
dependsOn(resourceMappingPatch)
|
||||
|
||||
apply {
|
||||
albumCardId = getResourceId(
|
||||
ResourceType.LAYOUT,
|
||||
"album_card",
|
||||
)
|
||||
albumCardId = ResourceType.LAYOUT["album_card"]
|
||||
|
||||
crowdfundingBoxId = getResourceId(
|
||||
ResourceType.LAYOUT,
|
||||
"donation_companion",
|
||||
)
|
||||
crowdfundingBoxId = ResourceType.LAYOUT["donation_companion"]
|
||||
|
||||
relatedChipCloudMarginId = getResourceId(
|
||||
ResourceType.LAYOUT,
|
||||
"related_chip_cloud_reduced_margins",
|
||||
)
|
||||
relatedChipCloudMarginId = ResourceType.LAYOUT["related_chip_cloud_reduced_margins"]
|
||||
|
||||
filterBarHeightId = getResourceId(
|
||||
ResourceType.DIMEN,
|
||||
"filter_bar_height",
|
||||
)
|
||||
filterBarHeightId = ResourceType.DIMEN["filter_bar_height"]
|
||||
|
||||
barContainerHeightId = getResourceId(
|
||||
ResourceType.DIMEN,
|
||||
"bar_container_height",
|
||||
)
|
||||
barContainerHeightId = ResourceType.DIMEN["bar_container_height"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,38 +227,40 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
|
||||
// region Mix playlists
|
||||
|
||||
parseElementFromBufferMethod.apply {
|
||||
val startIndex = parseElementFromBufferMethod.indices.first()
|
||||
val insertIndex = startIndex + 1
|
||||
parseElementFromBufferMethodMatch.let {
|
||||
it.method.apply {
|
||||
val startIndex = it.indices.first()
|
||||
val insertIndex = startIndex + 1
|
||||
|
||||
val byteArrayParameter = "p3"
|
||||
val conversionContextRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val returnEmptyComponentInstruction = instructions.last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
val returnEmptyComponentRegister = (returnEmptyComponentInstruction as FiveRegisterInstruction).registerC
|
||||
val freeRegister = findFreeRegister(insertIndex, conversionContextRegister, returnEmptyComponentRegister)
|
||||
val byteArrayParameter = "p3"
|
||||
val conversionContextRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val returnEmptyComponentInstruction = instructions.last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
val returnEmptyComponentRegister =
|
||||
(returnEmptyComponentInstruction as FiveRegisterInstruction).registerC
|
||||
val freeRegister =
|
||||
findFreeRegister(insertIndex, conversionContextRegister, returnEmptyComponentRegister)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :show
|
||||
move-object v$returnEmptyComponentRegister, p1 # Required for 19.47
|
||||
goto :return_empty_component
|
||||
:show
|
||||
nop
|
||||
""",
|
||||
ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
|
||||
)
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :show
|
||||
move-object v$returnEmptyComponentRegister, p1 # Required for 19.47
|
||||
goto :return_empty_component
|
||||
:show
|
||||
nop
|
||||
""",
|
||||
ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Watermark (legacy code for old versions of YouTube)
|
||||
|
||||
showWatermarkMethod.match(
|
||||
playerOverlayMethod.immutableClassDef,
|
||||
).method.apply {
|
||||
playerOverlayMethod.immutableClassDef.getShowWatermarkMethod().apply {
|
||||
val index = implementation!!.instructions.size - 5
|
||||
|
||||
removeInstruction(index)
|
||||
@@ -288,7 +277,7 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
|
||||
// region Show more button
|
||||
|
||||
(if (is_20_26_or_greater) hideShowMoreButtonMethod else hideShowMoreLegacyButtonMethod).let {
|
||||
(if (is_20_26_or_greater) hideShowMoreButtonMethodMatch else hideShowMoreLegacyButtonMethodMatch).let {
|
||||
it.method.apply {
|
||||
val moveRegisterIndex = it.indices.last()
|
||||
val viewRegister = getInstruction<OneRegisterInstruction>(moveRegisterIndex).registerA
|
||||
@@ -305,7 +294,7 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
// endregion
|
||||
|
||||
// region crowdfunding box
|
||||
crowdfundingBoxMethod.let {
|
||||
crowdfundingBoxMethodMatch.let {
|
||||
it.method.apply {
|
||||
val insertIndex = it.indices.last()
|
||||
val objectRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
@@ -322,7 +311,7 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
|
||||
// region hide album cards
|
||||
|
||||
albumCardsMethod.let {
|
||||
albumCardsMethodMatch.let {
|
||||
it.method.apply {
|
||||
val checkCastAnchorIndex = it.indices.last()
|
||||
val insertIndex = checkCastAnchorIndex + 1
|
||||
@@ -340,7 +329,7 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
|
||||
// region hide floating microphone
|
||||
|
||||
showFloatingMicrophoneButtonMethod.let {
|
||||
showFloatingMicrophoneButtonMethodMatch.let {
|
||||
it.method.apply {
|
||||
val index = it.indices.last()
|
||||
val register = getInstruction<TwoRegisterInstruction>(index).registerA
|
||||
@@ -378,8 +367,8 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
|
||||
// region hide view count
|
||||
|
||||
hideViewCountMethod.apply {
|
||||
val startIndex = hideViewCountMethod.patternMatch.startIndex
|
||||
hideViewCountMethodMatch.method.apply {
|
||||
val startIndex = hideViewCountMethodMatch.indices.first()
|
||||
var returnStringRegister = getInstruction<OneRegisterInstruction>(startIndex).registerA
|
||||
|
||||
// Find the instruction where the text dimension is retrieved.
|
||||
@@ -414,11 +403,11 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
* Patch a [Method] with a given [instructions].
|
||||
*
|
||||
* @param RegisterInstruction The type of instruction to get the register from.
|
||||
* @param insertIndexOffset The offset to add to the end index of the [Match.patternMatch].
|
||||
* @param insertIndexOffset The offset to add to the end index of the [MatchBuilder.indices].
|
||||
* @param hookRegisterOffset The offset to add to the register of the hook.
|
||||
* @param instructions The instructions to add with the register as a parameter.
|
||||
*/
|
||||
fun <RegisterInstruction : OneRegisterInstruction> Fingerprint.patch(
|
||||
fun <RegisterInstruction : OneRegisterInstruction> MatchBuilder.patch(
|
||||
insertIndexOffset: Int = 0,
|
||||
hookRegisterOffset: Int = 0,
|
||||
instructions: (Int) -> String,
|
||||
@@ -430,21 +419,21 @@ val `Hide layout components` by creatingBytecodePatch(
|
||||
addInstructions(insertIndex, instructions(register))
|
||||
}
|
||||
|
||||
filterBarHeightMethod.patch<TwoRegisterInstruction> { register ->
|
||||
filterBarHeightMethodMatch.patch<TwoRegisterInstruction> { register ->
|
||||
"""
|
||||
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I
|
||||
move-result v$register
|
||||
"""
|
||||
}
|
||||
|
||||
searchResultsChipBarMethod.patch<OneRegisterInstruction>(-1, -2) { register ->
|
||||
searchResultsChipBarMethodMatch.patch<OneRegisterInstruction>(-1, -2) { register ->
|
||||
"""
|
||||
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I
|
||||
move-result v$register
|
||||
"""
|
||||
}
|
||||
|
||||
relatedChipCloudMethod.patch<OneRegisterInstruction>(1) { register ->
|
||||
relatedChipCloudMethodMatch.patch<OneRegisterInstruction>(1) { register ->
|
||||
"invoke-static { v$register }, " +
|
||||
"$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V"
|
||||
}
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
package app.revanced.patches.youtube.layout.hide.infocards
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.infocardsIncognitoMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getInfocardsIncognitoMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Ljava/lang/Boolean;")
|
||||
parameterTypes("L", "J")
|
||||
instructions(
|
||||
"vibrator"(),
|
||||
)
|
||||
instructions("vibrator"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.infocardsIncognitoParentMethod by gettingFirstMethodDeclaratively {
|
||||
@@ -29,12 +23,12 @@ internal val BytecodePatchContext.infocardsIncognitoParentMethod by gettingFirst
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.infocardsMethodCallMethod by gettingFirstMethodDeclaratively {
|
||||
opcodes(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
)
|
||||
strings("Missing ControlsOverlayPresenter for InfoCards to work.")
|
||||
literal { drawerResourceId }
|
||||
}
|
||||
internal val infocardsMethodCallMethodMatch =
|
||||
firstMethodComposite("Missing ControlsOverlayPresenter for InfoCards to work.") {
|
||||
opcodes(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
)
|
||||
literal { drawerResourceId }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.ExternalLabel
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -27,10 +28,7 @@ private val hideInfocardsResourcePatch = resourcePatch {
|
||||
dependsOn(resourceMappingPatch)
|
||||
|
||||
apply {
|
||||
drawerResourceId = getResourceId(
|
||||
ResourceType.ID,
|
||||
"info_cards_drawer_header",
|
||||
)
|
||||
drawerResourceId = ResourceType.ID["info_cards_drawer_header"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +61,7 @@ val `Hide info cards` by creatingBytecodePatch(
|
||||
)
|
||||
|
||||
// Edit: This old non litho code may be obsolete and no longer used by any supported versions.
|
||||
infocardsIncognitoMethod.match(infocardsIncognitoParentMethod.immutableClassDef).method.apply {
|
||||
infocardsIncognitoParentMethod.immutableClassDef.getInfocardsIncognitoMethod().apply {
|
||||
val invokeInstructionIndex = implementation!!.instructions.indexOfFirst {
|
||||
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal &&
|
||||
((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V")
|
||||
@@ -77,7 +75,7 @@ val `Hide info cards` by creatingBytecodePatch(
|
||||
}
|
||||
|
||||
// Edit: This old non litho code may be obsolete and no longer used by any supported versions.
|
||||
infocardsMethodCallMethod.let {
|
||||
infocardsMethodCallMethodMatch.let {
|
||||
val invokeInterfaceIndex = it.indices.last()
|
||||
it.method.apply {
|
||||
val register = implementation!!.registerCount - 1
|
||||
|
||||
@@ -11,7 +11,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateMethod
|
||||
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateMethodMatch
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
@@ -45,7 +45,7 @@ val `Disable rolling number animations` by creatingBytecodePatch(
|
||||
|
||||
// Animations are disabled by preventing an Image from being applied to the text span,
|
||||
// which prevents the animations from appearing.
|
||||
rollingNumberTextViewAnimationUpdateMethod.let {
|
||||
rollingNumberTextViewAnimationUpdateMethodMatch.let {
|
||||
val blockStartIndex = it.indices.first()
|
||||
val blockEndIndex = it.indices.last() + 1
|
||||
it.method.apply {
|
||||
|
||||
@@ -2,19 +2,12 @@
|
||||
|
||||
package app.revanced.patches.youtube.layout.miniplayer
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.afterAtMost
|
||||
import app.revanced.patcher.checkCast
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
|
||||
|
||||
@@ -28,7 +21,7 @@ internal const val MINIPLAYER_INITIAL_SIZE_FEATURE_KEY = 45640023L
|
||||
internal const val MINIPLAYER_DISABLED_FEATURE_KEY = 45657015L
|
||||
internal const val MINIPLAYER_ANIMATED_EXPAND_FEATURE_KEY = 45644360L
|
||||
|
||||
internal val BytecodePatchContext.miniplayerModernConstructorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.miniplayerModernConstructorMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
45623000L(), // Magic number found in the constructor.
|
||||
@@ -56,7 +49,8 @@ internal val BytecodePatchContext.miniplayerModernViewParentMethod by gettingFir
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerModernAddViewListenerMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getMiniplayerModernAddViewListenerMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("Landroid/view/View;")
|
||||
@@ -65,33 +59,34 @@ internal val BytecodePatchContext.miniplayerModernAddViewListenerMethod by getti
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerModernCloseButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerModernCloseButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
ResourceType.ID("modern_miniplayer_close"),
|
||||
checkCast("Landroid/widget/ImageView;"),
|
||||
allOf(Opcode.CHECK_CAST(), "Landroid/widget/ImageView;"()),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerModernExpandButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerModernExpandButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
ResourceType.ID("modern_miniplayer_expand"),
|
||||
checkCast("Landroid/widget/ImageView;"),
|
||||
allOf(Opcode.CHECK_CAST(), "Landroid/widget/ImageView;"()),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerModernExpandCloseDrawablesMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getMiniplayerModernExpandCloseDrawablesMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
@@ -103,7 +98,7 @@ internal val BytecodePatchContext.miniplayerModernExpandCloseDrawablesMethod by
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerModernForwardButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerModernForwardButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes()
|
||||
@@ -113,7 +108,7 @@ internal val BytecodePatchContext.miniplayerModernForwardButtonMethod by getting
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.miniplayerModernOverlayViewMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerModernOverlayViewMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes()
|
||||
instructions(
|
||||
@@ -125,7 +120,7 @@ internal val BytecodePatchContext.miniplayerModernOverlayViewMethod by gettingFi
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerModernRewindButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerModernRewindButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes()
|
||||
@@ -138,7 +133,7 @@ internal val BytecodePatchContext.miniplayerModernRewindButtonMethod by gettingF
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerModernActionButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerModernActionButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes()
|
||||
@@ -148,7 +143,7 @@ internal val BytecodePatchContext.miniplayerModernActionButtonMethod by gettingF
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.miniplayerMinimumSizeMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerMinimumSizeMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.DIMEN("miniplayer_max_size"),
|
||||
@@ -157,20 +152,20 @@ internal val BytecodePatchContext.miniplayerMinimumSizeMethod by gettingFirstMet
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.miniplayerOverrideMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerOverrideMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
instructions(
|
||||
"appName"(),
|
||||
methodCall(
|
||||
parameters = listOf("Landroid/content/Context;"),
|
||||
returnType = "Z",
|
||||
afterAtMost(10),
|
||||
afterAtMost(
|
||||
10,
|
||||
method { parameterTypes.count() == 1 && parameterTypes.first() == "Landroid/content/Context;" && returnType == "Z" },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.miniplayerOverrideNoContextMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getMiniplayerOverrideNoContextMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
opcodes(
|
||||
@@ -181,7 +176,7 @@ internal val BytecodePatchContext.miniplayerOverrideNoContextMethod by gettingFi
|
||||
/**
|
||||
* 20.36 and lower. Codes appears to be removed in 20.37+
|
||||
*/
|
||||
internal val BytecodePatchContext.miniplayerResponseModelSizeCheckMethod by gettingFirstMethodDeclaratively {
|
||||
internal val miniplayerResponseModelSizeCheckMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes("Ljava/lang/Object;", "Ljava/lang/Object;")
|
||||
@@ -195,7 +190,7 @@ internal val BytecodePatchContext.miniplayerResponseModelSizeCheckMethod by gett
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.miniplayerOnCloseHandlerMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.miniplayerOnCloseHandlerMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
instructions(
|
||||
@@ -207,12 +202,10 @@ internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
|
||||
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"
|
||||
|
||||
internal val BytecodePatchContext.playerOverlaysLayoutMethod by gettingFirstMethodDeclaratively {
|
||||
custom { method, _ ->
|
||||
method.definingClass == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
|
||||
}
|
||||
definingClass(YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.miniplayerSetIconsMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.miniplayerSetIconsMethod by gettingFirstMutableMethodDeclaratively {
|
||||
returnType("V")
|
||||
parameterTypes("I", "Ljava/lang/Runnable;")
|
||||
instructions(
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
package app.revanced.patches.youtube.layout.miniplayer
|
||||
|
||||
import app.revanced.patcher.classDef
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -46,22 +48,12 @@ private val miniplayerResourcePatch = resourcePatch {
|
||||
apply {
|
||||
// Resource id is not used during patching, but is used by extension.
|
||||
// Verify the resource is present while patching.
|
||||
getResourceId(
|
||||
ResourceType.ID,
|
||||
"modern_miniplayer_subtitle_text",
|
||||
)
|
||||
ResourceType.ID["modern_miniplayer_subtitle_text"]
|
||||
|
||||
// Only required for exactly 19.16
|
||||
if (!is_19_17_or_greater) {
|
||||
ytOutlinePictureInPictureWhite24 = getResourceId(
|
||||
ResourceType.DRAWABLE,
|
||||
"yt_outline_picture_in_picture_white_24",
|
||||
)
|
||||
|
||||
ytOutlineXWhite24 = getResourceId(
|
||||
ResourceType.DRAWABLE,
|
||||
"yt_outline_x_white_24",
|
||||
)
|
||||
ytOutlinePictureInPictureWhite24 = ResourceType.DRAWABLE["yt_outline_picture_in_picture_white_24"]
|
||||
ytOutlineXWhite24 = ResourceType.DRAWABLE["yt_outline_x_white_24"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,31 +181,29 @@ val Miniplayer by creatingBytecodePatch(
|
||||
insertMiniplayerBooleanOverride(index, "getModernMiniplayerOverride")
|
||||
}
|
||||
|
||||
fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||
fun MutableMethod.insertMiniplayerFeatureFlagBooleanOverride(
|
||||
literal: Long,
|
||||
extensionMethod: String,
|
||||
) = method.insertLiteralOverride(
|
||||
) = insertLiteralOverride(
|
||||
literal,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z",
|
||||
)
|
||||
|
||||
fun Fingerprint.insertMiniplayerFeatureFlagFloatOverride(
|
||||
fun MutableMethod.insertMiniplayerFeatureFlagFloatOverride(
|
||||
literal: Long,
|
||||
extensionMethod: String,
|
||||
) {
|
||||
method.apply {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
|
||||
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
) = apply {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
|
||||
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
targetIndex + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
}
|
||||
addInstructions(
|
||||
targetIndex + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,20 +225,16 @@ val Miniplayer by creatingBytecodePatch(
|
||||
// Parts of the YT code is removed in 20.37+ and the legacy player no longer works.
|
||||
|
||||
if (!is_20_37_or_greater) {
|
||||
miniplayerOverrideNoContextMethod.match(
|
||||
miniplayerDimensionsCalculatorParentMethod.immutableClassDef,
|
||||
).method.apply {
|
||||
miniplayerDimensionsCalculatorParentMethod.immutableClassDef.getMiniplayerOverrideNoContextMethod().apply {
|
||||
findReturnIndicesReversed().forEach { index ->
|
||||
insertLegacyTabletMiniplayerOverride(
|
||||
index,
|
||||
)
|
||||
insertLegacyTabletMiniplayerOverride(index)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Legacy tablet miniplayer hooks.
|
||||
miniplayerOverrideMethod.let {
|
||||
miniplayerOverrideMethodMatch.let {
|
||||
val appNameStringIndex = it.indices.last()
|
||||
navigate(it.immutableMethod).to(appNameStringIndex).stop().apply {
|
||||
findReturnIndicesReversed().forEach { index ->
|
||||
@@ -259,7 +245,7 @@ val Miniplayer by creatingBytecodePatch(
|
||||
}
|
||||
}
|
||||
|
||||
miniplayerResponseModelSizeCheckMethod.let {
|
||||
miniplayerResponseModelSizeCheckMethodMatch.let {
|
||||
it.method.insertLegacyTabletMiniplayerOverride(it.indices.last())
|
||||
}
|
||||
}
|
||||
@@ -324,7 +310,7 @@ val Miniplayer by creatingBytecodePatch(
|
||||
}
|
||||
|
||||
// Override a minimum size constant.
|
||||
miniplayerMinimumSizeMethod.let {
|
||||
miniplayerMinimumSizeMethodMatch.let {
|
||||
it.method.apply {
|
||||
val index = it.indices[1]
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
@@ -367,9 +353,7 @@ val Miniplayer by creatingBytecodePatch(
|
||||
// YT fixed this mistake in 19.17.
|
||||
// Fix this, by swapping the drawable resource values with each other.
|
||||
if (!is_19_17_or_greater) {
|
||||
miniplayerModernExpandCloseDrawablesMethod.match(
|
||||
miniplayerModernViewParentMethod.immutableClassDef,
|
||||
).method.apply {
|
||||
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernExpandCloseDrawablesMethod().apply {
|
||||
listOf(
|
||||
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
|
||||
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
|
||||
@@ -411,17 +395,15 @@ val Miniplayer by creatingBytecodePatch(
|
||||
// region Add hooks to hide modern miniplayer buttons.
|
||||
|
||||
listOf(
|
||||
miniplayerModernExpandButtonMethod to "hideMiniplayerExpandClose",
|
||||
miniplayerModernCloseButtonMethod to "hideMiniplayerExpandClose",
|
||||
miniplayerModernActionButtonMethod to "hideMiniplayerActionButton",
|
||||
miniplayerModernRewindButtonMethod to "hideMiniplayerRewindForward",
|
||||
miniplayerModernForwardButtonMethod to "hideMiniplayerRewindForward",
|
||||
miniplayerModernOverlayViewMethod to "adjustMiniplayerOpacity",
|
||||
).forEach { (fingerprint, methodName) ->
|
||||
fingerprint.match(
|
||||
miniplayerModernViewParentMethod.classDef,
|
||||
).method.apply {
|
||||
val index = fingerprint.indices.last()
|
||||
miniplayerModernExpandButtonMethodMatch to "hideMiniplayerExpandClose",
|
||||
miniplayerModernCloseButtonMethodMatch to "hideMiniplayerExpandClose",
|
||||
miniplayerModernActionButtonMethodMatch to "hideMiniplayerActionButton",
|
||||
miniplayerModernRewindButtonMethodMatch to "hideMiniplayerRewindForward",
|
||||
miniplayerModernForwardButtonMethodMatch to "hideMiniplayerRewindForward",
|
||||
miniplayerModernOverlayViewMethodMatch to "adjustMiniplayerOpacity",
|
||||
).forEach { (match, methodName) ->
|
||||
match.match(miniplayerModernViewParentMethod.immutableClassDef).method.apply {
|
||||
val index = match.indices.last()
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstruction(
|
||||
@@ -431,9 +413,7 @@ val Miniplayer by creatingBytecodePatch(
|
||||
}
|
||||
}
|
||||
|
||||
miniplayerModernAddViewListenerMethod.match(
|
||||
miniplayerModernViewParentMethod.classDef,
|
||||
).method.addInstruction(
|
||||
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernAddViewListenerMethod().addInstruction(
|
||||
0,
|
||||
"invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"hideMiniplayerSubTexts(Landroid/view/View;)V",
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
/**
|
||||
* 19.46+
|
||||
*/
|
||||
internal val BytecodePatchContext.openVideosFullscreenPortraitMethod by gettingFirstMethodDeclaratively {
|
||||
internal val openVideosFullscreenPortraitMethodMatch = firstMethodComposite {
|
||||
returnType("V")
|
||||
parameterTypes("L", "Lj\$/util/Optional;")
|
||||
instructions(
|
||||
@@ -22,7 +22,7 @@ internal val BytecodePatchContext.openVideosFullscreenPortraitMethod by gettingF
|
||||
/**
|
||||
* Pre 19.46.
|
||||
*/
|
||||
internal val BytecodePatchContext.openVideosFullscreenPortraitLegacyMethod by gettingFirstMethodDeclaratively {
|
||||
internal val openVideosFullscreenPortraitLegacyMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L", "Lj\$/util/Optional;")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||
|
||||
import app.revanced.patcher.MatchBuilder
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
@@ -23,14 +24,14 @@ internal val openVideosFullscreenHookPatch = bytecodePatch {
|
||||
)
|
||||
|
||||
apply {
|
||||
var fingerprint: Fingerprint
|
||||
var match: MatchBuilder
|
||||
var insertIndex: Int
|
||||
|
||||
if (is_19_46_or_greater) {
|
||||
fingerprint = openVideosFullscreenPortraitMethod
|
||||
insertIndex = fingerprint.indices.first()
|
||||
match = openVideosFullscreenPortraitMethodMatch
|
||||
insertIndex = match.indices.first()
|
||||
|
||||
openVideosFullscreenPortraitMethod.let {
|
||||
openVideosFullscreenPortraitMethodMatch.let {
|
||||
// Remove A/B feature call that forces what this patch already does.
|
||||
// Cannot use the A/B flag to accomplish the same goal because 19.50+
|
||||
// Shorts fullscreen regular player does not use fullscreen
|
||||
@@ -41,22 +42,20 @@ internal val openVideosFullscreenHookPatch = bytecodePatch {
|
||||
)
|
||||
}
|
||||
} else {
|
||||
fingerprint = openVideosFullscreenPortraitLegacyMethod
|
||||
insertIndex = fingerprint.indices.last()
|
||||
match = openVideosFullscreenPortraitLegacyMethodMatch
|
||||
insertIndex = match.indices.last()
|
||||
}
|
||||
|
||||
fingerprint.let {
|
||||
it.method.apply {
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
match.method.apply {
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->doNotOpenVideoFullscreenPortrait(Z)Z
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
}
|
||||
addInstructions(
|
||||
insertIndex + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->doNotOpenVideoFullscreenPortrait(Z)Z
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ val `Custom player overlay opacity` by creatingBytecodePatch(
|
||||
TextPreference("revanced_player_overlay_opacity", inputType = InputType.NUMBER),
|
||||
)
|
||||
|
||||
createPlayerOverviewMethod.let {
|
||||
createPlayerOverviewMethodMatch.let {
|
||||
it.method.apply {
|
||||
val viewRegisterIndex = it.indices.last()
|
||||
val viewRegister = getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package app.revanced.patches.youtube.layout.player.overlay
|
||||
|
||||
import app.revanced.patcher.checkCast
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.createPlayerOverviewMethod by gettingFirstMethodDeclaratively {
|
||||
internal val createPlayerOverviewMethodMatch = firstMethodComposite {
|
||||
returnType("V")
|
||||
instructions(
|
||||
ResourceType.ID("scrim_overlay"),
|
||||
checkCast("Landroid/widget/ImageView;", afterAtMost(10)),
|
||||
afterAtMost(10, allOf(Opcode.CHECK_CAST(), type("Landroid/widget/ImageView;"))),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,43 +1,33 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.dislikeMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.dislikeMethod by gettingFirstMutableMethodDeclaratively {
|
||||
returnType("V")
|
||||
instructions(
|
||||
"like/dislike"(),
|
||||
)
|
||||
instructions("like/dislike"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.likeMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.likeMethod by gettingFirstMutableMethodDeclaratively {
|
||||
returnType("V")
|
||||
instructions(
|
||||
"like/like"(),
|
||||
)
|
||||
instructions("like/like"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.removeLikeMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.removeLikeMethod by gettingFirstMutableMethodDeclaratively {
|
||||
returnType("V")
|
||||
instructions(
|
||||
"like/removelike"(),
|
||||
)
|
||||
instructions("like/removelike"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.rollingNumberMeasureAnimatedTextMethod by gettingFirstMethodDeclaratively {
|
||||
internal val rollingNumberMeasureAnimatedTextMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returnType("Lj\$/util/Optional;")
|
||||
parameterTypes("L", "Ljava/lang/String;", "L")
|
||||
opcodes(
|
||||
Opcode.IGET, // First instruction of method
|
||||
Opcode.IGET, // First instruction of method.
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_HIGH16,
|
||||
@@ -46,14 +36,14 @@ internal val BytecodePatchContext.rollingNumberMeasureAnimatedTextMethod by gett
|
||||
Opcode.CONST_4,
|
||||
Opcode.AGET,
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_4, // Measured text width
|
||||
Opcode.CONST_4, // Measured text width.
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches to class found in [rollingNumberMeasureStaticLabelParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.rollingNumberMeasureStaticLabelMethod by gettingFirstMethodDeclaratively {
|
||||
internal val rollingNumberMeasureStaticLabelMethod = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("F")
|
||||
parameterTypes("Ljava/lang/String;")
|
||||
@@ -74,29 +64,33 @@ internal val BytecodePatchContext.rollingNumberMeasureStaticLabelParentMethod by
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.rollingNumberSetterMethod by gettingFirstMethodDeclaratively {
|
||||
internal val rollingNumberSetterMethodMatch = firstMethodComposite {
|
||||
opcodes(
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
)
|
||||
// Partial string match.
|
||||
strings("RollingNumberType required properties missing! Need")
|
||||
custom {
|
||||
instructions.matchIndexed(
|
||||
"string",
|
||||
"RollingNumberType required properties missing! Need"(String::contains),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.rollingNumberTextViewMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.rollingNumberTextViewMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L", "F", "F")
|
||||
opcodes(
|
||||
Opcode.IPUT,
|
||||
null, // invoke-direct or invoke-virtual
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID,
|
||||
instructions(
|
||||
Opcode.IPUT(),
|
||||
anyOf(Opcode.INVOKE_DIRECT(), Opcode.INVOKE_VIRTUAL()),
|
||||
Opcode.IPUT_OBJECT(),
|
||||
Opcode.IGET_OBJECT(),
|
||||
Opcode.INVOKE_VIRTUAL(),
|
||||
Opcode.RETURN_VOID(),
|
||||
)
|
||||
custom { _, classDef ->
|
||||
classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" || classDef.superclass ==
|
||||
custom {
|
||||
immutableClassDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" || immutableClassDef.superclass ==
|
||||
"Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;"
|
||||
}
|
||||
}
|
||||
@@ -114,28 +108,23 @@ internal val BytecodePatchContext.textComponentDataMethod by gettingFirstMethodD
|
||||
instructions(
|
||||
"text"(),
|
||||
)
|
||||
custom { _, classDef ->
|
||||
classDef.fields.find { it.type == "Ljava/util/BitSet;" } != null
|
||||
}
|
||||
custom { immutableClassDef.anyField { type == "Ljava/util/BitSet;" } }
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches against the same class found in [textComponentConstructorMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.textComponentLookupMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getTextComponentLookupMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes("L")
|
||||
instructions(
|
||||
"…"(),
|
||||
)
|
||||
instructions("…"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.textComponentFeatureFlagMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.textComponentFeatureFlagMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
45675738L(),
|
||||
)
|
||||
instructions(45675738L())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.fieldReference
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -18,21 +21,20 @@ import app.revanced.patches.youtube.misc.playservice.*
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.conversionContextToStringMethod
|
||||
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateMethod
|
||||
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateMethodMatch
|
||||
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
|
||||
import app.revanced.patches.youtube.video.videoid.hookVideoId
|
||||
import app.revanced.patches.youtube.video.videoid.videoIdPatch
|
||||
import app.revanced.util.*
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.returnLate
|
||||
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
|
||||
import java.util.logging.Logger
|
||||
import kotlin.jvm.java
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch;"
|
||||
@@ -102,8 +104,8 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
|
||||
likeMethod to Vote.LIKE,
|
||||
dislikeMethod to Vote.DISLIKE,
|
||||
removeLikeMethod to Vote.REMOVE_LIKE,
|
||||
).forEach { (fingerprint, vote) ->
|
||||
fingerprint.method.addInstructions(
|
||||
).forEach { (method, vote) ->
|
||||
method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, ${vote.value}
|
||||
@@ -128,7 +130,7 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
|
||||
it.type == conversionContextClass.superclass
|
||||
} ?: throw PatchException("Could not find conversion context field")
|
||||
|
||||
textComponentLookupMethod.match(textComponentConstructorMethod.immutableClassDef).method.apply {
|
||||
textComponentConstructorMethod.immutableClassDef.getTextComponentLookupMethod().apply {
|
||||
// Find the instruction for creating the text data object.
|
||||
val textDataClassType = textComponentDataMethod.immutableClassDef.type
|
||||
|
||||
@@ -138,24 +140,24 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
|
||||
if (is_19_33_or_greater && !is_20_10_or_greater) {
|
||||
val index = indexOfFirstInstructionOrThrow {
|
||||
(opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) &&
|
||||
getReference<MethodReference>()?.returnType == textDataClassType
|
||||
methodReference?.returnType == textDataClassType
|
||||
}
|
||||
|
||||
insertIndex = indexOfFirstInstructionOrThrow(index) {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>()?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;"
|
||||
methodReference?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;"
|
||||
}
|
||||
|
||||
charSequenceRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
|
||||
} else {
|
||||
insertIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.NEW_INSTANCE &&
|
||||
getReference<TypeReference>()?.type == textDataClassType
|
||||
fieldReference?.type == textDataClassType
|
||||
}
|
||||
|
||||
val charSequenceIndex = indexOfFirstInstructionOrThrow(insertIndex) {
|
||||
opcode == Opcode.IPUT_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/CharSequence;"
|
||||
fieldReference?.type == "Ljava/lang/CharSequence;"
|
||||
}
|
||||
charSequenceRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerA
|
||||
}
|
||||
@@ -221,13 +223,11 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
|
||||
|
||||
// region Hook rolling numbers.
|
||||
|
||||
rollingNumberSetterMethod.apply {
|
||||
rollingNumberSetterMethodMatch.method.apply {
|
||||
val insertIndex = 1
|
||||
val dislikesIndex = rollingNumberSetterMethod.indices.last()
|
||||
val charSequenceInstanceRegister =
|
||||
getInstruction<OneRegisterInstruction>(0).registerA
|
||||
val charSequenceFieldReference =
|
||||
getInstruction<ReferenceInstruction>(dislikesIndex).reference
|
||||
val dislikesIndex = rollingNumberSetterMethodMatch.indices.last()
|
||||
val charSequenceInstanceRegister = getInstruction<OneRegisterInstruction>(0).registerA
|
||||
val charSequenceFieldReference = getInstruction<ReferenceInstruction>(dislikesIndex).reference
|
||||
|
||||
val conversionContextRegister = implementation!!.registerCount - parameters.size + 1
|
||||
|
||||
@@ -246,7 +246,7 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
|
||||
|
||||
// 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.
|
||||
rollingNumberMeasureAnimatedTextMethod.let {
|
||||
rollingNumberMeasureAnimatedTextMethodMatch.let {
|
||||
// Additional check to verify the opcodes are at the start of the method
|
||||
if (it.indices.first() != 0) throw PatchException("Unexpected opcode location")
|
||||
val endIndex = it.indices.last()
|
||||
@@ -290,11 +290,11 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
|
||||
rollingNumberTextViewMethod,
|
||||
// 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.
|
||||
rollingNumberTextViewAnimationUpdateMethod,
|
||||
rollingNumberTextViewAnimationUpdateMethodMatch.method,
|
||||
).forEach { insertMethod ->
|
||||
insertMethod.apply {
|
||||
val setTextIndex = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.name == "setText"
|
||||
methodReference?.name == "setText"
|
||||
}
|
||||
|
||||
val textViewRegister = getInstruction<FiveRegisterInstruction>(setTextIndex).registerC
|
||||
|
||||
@@ -1,33 +1,22 @@
|
||||
package app.revanced.patches.youtube.layout.seekbar
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.afterAtMost
|
||||
import app.revanced.patcher.anyInstruction
|
||||
import app.revanced.patcher.custom
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.literal
|
||||
import app.revanced.patcher.method
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.fullscreenSeekbarThumbnailsMethod by gettingFirstMethodDeclaratively {
|
||||
returnType("Z")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes()
|
||||
instructions(
|
||||
45398577(),
|
||||
45398577L(),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.playerSeekbarColorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val playerSeekbarColorMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.COLOR("inline_time_bar_played_not_highlighted_color"),
|
||||
@@ -36,19 +25,24 @@ internal val BytecodePatchContext.playerSeekbarColorMethod by gettingFirstMethod
|
||||
}
|
||||
|
||||
// class is ControlsOverlayStyle in 20.32 and lower, and obfuscated in 20.33+
|
||||
internal val BytecodePatchContext.setSeekbarClickedColorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val setSeekbarClickedColorMethodMatch = firstMethodComposite(
|
||||
"YOUTUBE",
|
||||
"PREROLL",
|
||||
"POSTROLL",
|
||||
"REMOTE_LIVE",
|
||||
"AD_LARGE_CONTROLS",
|
||||
) {
|
||||
opcodes(Opcode.CONST_HIGH16)
|
||||
strings("YOUTUBE", "PREROLL", "POSTROLL", "REMOTE_LIVE", "AD_LARGE_CONTROLS")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.shortsSeekbarColorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val shortsSeekbarColorMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.COLOR("reel_time_bar_played_color"),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.playerSeekbarHandle1ColorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val playerSeekbarHandle1ColorMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.COLOR("inline_time_bar_live_seekable_range"),
|
||||
@@ -56,7 +50,7 @@ internal val BytecodePatchContext.playerSeekbarHandle1ColorMethod by gettingFirs
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.playerSeekbarHandle2ColorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val playerSeekbarHandle2ColorMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameterTypes("Landroid/content/Context;")
|
||||
instructions(
|
||||
@@ -65,18 +59,18 @@ internal val BytecodePatchContext.playerSeekbarHandle2ColorMethod by gettingFirs
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.watchHistoryMenuUseProgressDrawableMethod by gettingFirstMethodDeclaratively {
|
||||
internal val watchHistoryMenuUseProgressDrawableMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
instructions(
|
||||
methodCall("Landroid/widget/ProgressBar;", "setMax"),
|
||||
method { name == "setMax" && definingClass == "Landroid/widget/ProgressBar;" },
|
||||
Opcode.MOVE_RESULT(),
|
||||
-1712394514(),
|
||||
(-1712394514L)(),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.lithoLinearGradientMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.lithoLinearGradientMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.STATIC)
|
||||
returnType("Landroid/graphics/LinearGradient;")
|
||||
parameterTypes("F", "F", "F", "F", "[I", "[F")
|
||||
@@ -85,7 +79,7 @@ internal val BytecodePatchContext.lithoLinearGradientMethod by gettingFirstMetho
|
||||
/**
|
||||
* 19.49+
|
||||
*/
|
||||
internal val BytecodePatchContext.playerLinearGradientMethod by gettingFirstMethodDeclaratively {
|
||||
internal val playerLinearGradientMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
parameterTypes("I", "I", "I", "I", "Landroid/content/Context;", "I")
|
||||
returnType("Landroid/graphics/LinearGradient;")
|
||||
@@ -100,7 +94,7 @@ internal val BytecodePatchContext.playerLinearGradientMethod by gettingFirstMeth
|
||||
/**
|
||||
* 19.25 - 19.47
|
||||
*/
|
||||
internal val BytecodePatchContext.playerLinearGradientLegacyMethod by gettingFirstMethodDeclaratively {
|
||||
internal val playerLinearGradientLegacyMethodMatch = firstMethodComposite {
|
||||
returnType("V")
|
||||
instructions(
|
||||
ResourceType.COLOR("yt_youtube_magenta"),
|
||||
@@ -142,11 +136,12 @@ internal val BytecodePatchContext.lottieCompositionFactoryZipMethod by gettingFi
|
||||
*
|
||||
* [Original method](https://github.com/airbnb/lottie-android/blob/26ad8bab274eac3f93dccccfa0cafc39f7408d13/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java#L386)
|
||||
*/
|
||||
internal val BytecodePatchContext.lottieCompositionFactoryFromJsonInputStreamMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getLottieCompositionFactoryFromJsonInputStreamMethod() = firstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
parameterTypes("Ljava/io/InputStream;", "Ljava/lang/String;")
|
||||
returnType("L")
|
||||
instructions(
|
||||
anyInstruction(literal(2), literal(3)),
|
||||
anyOf(2L(), 3L()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.youtube.layout.seekbar
|
||||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
@@ -47,19 +48,19 @@ val seekbarColorPatch = bytecodePatch(
|
||||
)
|
||||
}
|
||||
|
||||
playerSeekbarColorMethod.let {
|
||||
playerSeekbarColorMethodMatch.let {
|
||||
it.method.apply {
|
||||
addColorChangeInstructions(it.indices.last())
|
||||
addColorChangeInstructions(it.indices.first())
|
||||
}
|
||||
}
|
||||
|
||||
shortsSeekbarColorMethod.let {
|
||||
shortsSeekbarColorMethodMatch.let {
|
||||
it.method.addColorChangeInstructions(it.indices.first())
|
||||
}
|
||||
|
||||
setSeekbarClickedColorMethod.immutableMethod.let {
|
||||
val setColorMethodIndex = setSeekbarClickedColorMethod.indices.first() + 1
|
||||
setSeekbarClickedColorMethodMatch.immutableMethod.let {
|
||||
val setColorMethodIndex = setSeekbarClickedColorMethodMatch.indices.first() + 1
|
||||
|
||||
navigate(it).to(setColorMethodIndex).stop().apply {
|
||||
val colorRegister = getInstruction<TwoRegisterInstruction>(0).registerA
|
||||
@@ -77,9 +78,9 @@ val seekbarColorPatch = bytecodePatch(
|
||||
|
||||
// 19.25+ changes
|
||||
|
||||
var handleBarColorFingerprints = mutableListOf(playerSeekbarHandle1ColorMethod)
|
||||
var handleBarColorFingerprints = mutableListOf(playerSeekbarHandle1ColorMethodMatch)
|
||||
if (!is_20_34_or_greater) {
|
||||
handleBarColorFingerprints += playerSeekbarHandle2ColorMethod
|
||||
handleBarColorFingerprints += playerSeekbarHandle2ColorMethodMatch
|
||||
}
|
||||
handleBarColorFingerprints.forEach {
|
||||
it.method.addColorChangeInstructions(it.indices.last())
|
||||
@@ -89,7 +90,7 @@ val seekbarColorPatch = bytecodePatch(
|
||||
// of the watch history menu items as they use the same gradient as the
|
||||
// player and there is no easy way to distinguish which to use a transparent color.
|
||||
if (is_19_34_or_greater) {
|
||||
watchHistoryMenuUseProgressDrawableMethod.let {
|
||||
watchHistoryMenuUseProgressDrawableMethodMatch.let {
|
||||
it.method.apply {
|
||||
val index = it.indices[1]
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
@@ -113,17 +114,17 @@ val seekbarColorPatch = bytecodePatch(
|
||||
""",
|
||||
)
|
||||
|
||||
val playerFingerprint: Fingerprint
|
||||
val playerMatch: MatchBuilder
|
||||
val checkGradientCoordinates: Boolean
|
||||
if (is_19_49_or_greater) {
|
||||
playerFingerprint = playerLinearGradientMethod
|
||||
playerMatch = playerLinearGradientMethodMatch
|
||||
checkGradientCoordinates = true
|
||||
} else {
|
||||
playerFingerprint = playerLinearGradientLegacyMethod
|
||||
playerMatch = playerLinearGradientLegacyMethodMatch
|
||||
checkGradientCoordinates = false
|
||||
}
|
||||
|
||||
playerFingerprint.let {
|
||||
playerMatch.let {
|
||||
it.method.apply {
|
||||
val index = it.indices.last()
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
@@ -153,8 +154,7 @@ val seekbarColorPatch = bytecodePatch(
|
||||
|
||||
// Hook the splash animation to set the a seekbar color.
|
||||
mainActivityOnCreateMethod.apply {
|
||||
val setAnimationIntMethodName =
|
||||
lottieAnimationViewSetAnimationIntMethod.immutableMethod.name
|
||||
val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntMethod.name
|
||||
|
||||
findInstructionIndicesReversedOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
@@ -175,8 +175,7 @@ val seekbarColorPatch = bytecodePatch(
|
||||
// and `setAnimation(InputStream, String)` so extension code can call them.
|
||||
lottieAnimationViewSetAnimationIntMethod.classDef.methods.apply {
|
||||
val addedMethodName = "patch_setAnimation"
|
||||
val setAnimationIntName = lottieAnimationViewSetAnimationIntMethod
|
||||
.immutableMethod.name
|
||||
val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntMethod.name
|
||||
|
||||
add(
|
||||
ImmutableMethod(
|
||||
@@ -191,9 +190,9 @@ val seekbarColorPatch = bytecodePatch(
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
"""
|
||||
invoke-virtual { p0, p1 }, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationIntName(I)V
|
||||
return-void
|
||||
""",
|
||||
invoke-virtual { p0, p1 }, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationIntMethodName(I)V
|
||||
return-void
|
||||
""",
|
||||
)
|
||||
},
|
||||
)
|
||||
@@ -201,24 +200,21 @@ val seekbarColorPatch = bytecodePatch(
|
||||
val factoryStreamClass: CharSequence
|
||||
val factoryStreamName: CharSequence
|
||||
val factoryStreamReturnType: CharSequence
|
||||
lottieCompositionFactoryFromJsonInputStreamMethod.match(
|
||||
lottieCompositionFactoryZipMethod.immutableClassDef,
|
||||
).immutableMethod.apply {
|
||||
factoryStreamClass = definingClass
|
||||
factoryStreamName = name
|
||||
factoryStreamReturnType = returnType
|
||||
}
|
||||
|
||||
val lottieAnimationViewSetAnimationStreamFingerprint = fingerprint {
|
||||
lottieCompositionFactoryZipMethod.immutableClassDef.getLottieCompositionFactoryFromJsonInputStreamMethod()
|
||||
.let {
|
||||
factoryStreamClass = it.definingClass
|
||||
factoryStreamName = it.name
|
||||
factoryStreamReturnType = it.returnType
|
||||
}
|
||||
|
||||
val lottieAnimationViewSetAnimationStreamMethod = firstMutableMethodDeclaratively {
|
||||
definingClass(lottieAnimationViewSetAnimationIntMethod.immutableClassDef.type)
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes(factoryStreamReturnType.toString())
|
||||
returnType("V")
|
||||
custom { _, classDef ->
|
||||
classDef.type == lottieAnimationViewSetAnimationIntMethod.immutableClassDef.type
|
||||
}
|
||||
}
|
||||
val setAnimationStreamName = lottieAnimationViewSetAnimationStreamFingerprint
|
||||
.immutableMethod.name
|
||||
val setAnimationStreamMethodName = lottieAnimationViewSetAnimationStreamMethod.name
|
||||
|
||||
add(
|
||||
ImmutableMethod(
|
||||
@@ -236,11 +232,11 @@ val seekbarColorPatch = bytecodePatch(
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
"""
|
||||
invoke-static { p1, p2 }, $factoryStreamClass->$factoryStreamName(Ljava/io/InputStream;Ljava/lang/String;)$factoryStreamReturnType
|
||||
move-result-object v0
|
||||
invoke-virtual { p0, v0}, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationStreamName($factoryStreamReturnType)V
|
||||
return-void
|
||||
""",
|
||||
invoke-static { p1, p2 }, $factoryStreamClass->$factoryStreamName(Ljava/io/InputStream;Ljava/lang/String;)$factoryStreamReturnType
|
||||
move-result-object v0
|
||||
invoke-virtual { p0, v0}, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationStreamMethodName($factoryStreamReturnType)V
|
||||
return-void
|
||||
""",
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
package app.revanced.patches.youtube.layout.shortsautoplay
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.afterAtMost
|
||||
import app.revanced.patcher.field
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.method
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val reelEnumConstructorMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
@@ -36,7 +28,8 @@ internal val BytecodePatchContext.reelPlaybackRepeatParentMethod by gettingFirst
|
||||
/**
|
||||
* Matches class found in [reelPlaybackRepeatParentMethod].
|
||||
*/
|
||||
internal val BytecodePatchContext.reelPlaybackRepeatMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getReelPlaybackRepeatMethod() = firstMutableMethodDeclaratively {
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
instructions(method { toString() == "Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z" })
|
||||
@@ -54,14 +47,15 @@ internal val reelPlaybackMethodMatch = firstMethodComposite {
|
||||
15,
|
||||
method {
|
||||
name == "<init>" &&
|
||||
parameterTypes.zip(methodParametersPrefix).all { (a, b) -> a.startsWith(b) }
|
||||
parameterTypes.zip(methodParametersPrefix).all { (a, b) -> a.startsWith(b) }
|
||||
},
|
||||
),
|
||||
methodCall(
|
||||
opcode = Opcode.INVOKE_VIRTUAL,
|
||||
parameters = listOf("L"),
|
||||
returnType = "I",
|
||||
afterAtMost(5),
|
||||
afterAtMost(
|
||||
5,
|
||||
allOf(
|
||||
Opcode.INVOKE_VIRTUAL(),
|
||||
method { returnType == "I" && parameterTypes.count() == 1 && parameterTypes.first() == "L" },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.youtube.layout.shortsautoplay
|
||||
|
||||
import app.revanced.patcher.classDef
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
@@ -88,15 +89,15 @@ val `Shorts autoplay` by creatingBytecodePatch(
|
||||
)
|
||||
}
|
||||
|
||||
reelPlaybackRepeatMethod.match(
|
||||
reelPlaybackRepeatParentMethod.immutableClassDef,
|
||||
).method.apply {
|
||||
val reelPlaybackRepeatMethod = reelPlaybackRepeatParentMethod.immutableClassDef.getReelPlaybackRepeatMethod()
|
||||
|
||||
reelPlaybackRepeatMethod.apply {
|
||||
// The behavior enums are looked up from an ordinal value to an enum type.
|
||||
findInstructionIndicesReversedOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == reelEnumClass &&
|
||||
reference.parameterTypes.firstOrNull() == "I" &&
|
||||
reference.returnType == reelEnumClass
|
||||
reference.parameterTypes.firstOrNull() == "I" &&
|
||||
reference.returnType == reelEnumClass
|
||||
}.forEach { index ->
|
||||
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
|
||||
@@ -123,14 +124,14 @@ val `Shorts autoplay` by creatingBytecodePatch(
|
||||
// Find the first call modified by extension code above.
|
||||
val extensionReturnResultIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_STATIC &&
|
||||
getReference<MethodReference>()?.definingClass == EXTENSION_CLASS_DESCRIPTOR
|
||||
getReference<MethodReference>()?.definingClass == EXTENSION_CLASS_DESCRIPTOR
|
||||
} + 1
|
||||
val enumRegister = getInstruction<OneRegisterInstruction>(extensionReturnResultIndex).registerA
|
||||
val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<FieldReference>()
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
reference?.definingClass == definingClass &&
|
||||
reference.type == reelSequenceControllerMethodReference.definingClass
|
||||
reference?.definingClass == definingClass &&
|
||||
reference.type == reelSequenceControllerMethodReference.definingClass
|
||||
}
|
||||
val getReelSequenceControllerReference =
|
||||
getInstruction<ReferenceInstruction>(getReelSequenceControllerIndex).reference
|
||||
|
||||
@@ -6,7 +6,7 @@ import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.toolBarButtonMethod by gettingFirstMethodDeclaratively {
|
||||
internal val toolBarButtonMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
instructions(
|
||||
@@ -21,7 +21,7 @@ internal val BytecodePatchContext.toolBarButtonMethod by gettingFirstMethodDecla
|
||||
custom { parameterTypes.count() in 1..2 && parameterTypes.first() == "Landroid/view/MenuItem;" }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.spoofAppVersionMethod by gettingFirstMethodDeclaratively(
|
||||
internal val spoofAppVersionMethodMatch = firstMethodComposite(
|
||||
// Instead of applying a bytecode patch, it might be possible to only rely on code from the extension and
|
||||
// manually set the desired version string as this keyed value in the SharedPreferences.
|
||||
// But, this bytecode patch is simple and it works.
|
||||
|
||||
@@ -81,23 +81,23 @@ val `Spoof app version` by creatingBytecodePatch(
|
||||
* missing image resources. As a workaround, do not set an image in the
|
||||
* toolbar when the enum name is UNKNOWN.
|
||||
*/
|
||||
toolBarButtonMethod.apply {
|
||||
val imageResourceIndex = indices[2]
|
||||
val register = method.getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
|
||||
val jumpIndex = indices.last() + 1
|
||||
toolBarButtonMethodMatch.let {
|
||||
val imageResourceIndex = it.indices[2]
|
||||
val register = it.method.getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
|
||||
val jumpIndex = it.indices.last() + 1
|
||||
|
||||
method.addInstructionsWithLabels(
|
||||
it.method.addInstructionsWithLabels(
|
||||
imageResourceIndex + 1,
|
||||
"if-eqz v$register, :ignore",
|
||||
ExternalLabel("ignore", method.getInstruction(jumpIndex)),
|
||||
ExternalLabel("ignore", it.method.getInstruction(jumpIndex)),
|
||||
)
|
||||
}
|
||||
|
||||
spoofAppVersionMethod.apply {
|
||||
val index = indices.first()
|
||||
val register = method.getInstruction<OneRegisterInstruction>(index).registerA
|
||||
spoofAppVersionMethodMatch.let {
|
||||
val index = it.indices.first()
|
||||
val register = it.method.getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
method.addInstructions(
|
||||
it.method.addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.patches.youtube.layout.startupshortsreset
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.patch.creatingBytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
@@ -49,11 +50,10 @@ val `Disable resuming Shorts on startup` by creatingBytecodePatch(
|
||||
)
|
||||
|
||||
if (is_20_03_or_greater) {
|
||||
userWasInShortsAlternativeMethod.let {
|
||||
userWasInShortsAlternativeMethodMatch.let {
|
||||
it.method.apply {
|
||||
val match = it.instructionMatches[2]
|
||||
val insertIndex = match.index + 1
|
||||
val register = match.getInstruction<OneRegisterInstruction>().registerA
|
||||
val insertIndex = it.indices[2] + 1
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
@@ -68,8 +68,8 @@ val `Disable resuming Shorts on startup` by creatingBytecodePatch(
|
||||
userWasInShortsLegacyMethod.apply {
|
||||
val listenableInstructionIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_INTERFACE &&
|
||||
getReference<MethodReference>()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" &&
|
||||
getReference<MethodReference>()?.name == "isDone"
|
||||
getReference<MethodReference>()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" &&
|
||||
getReference<MethodReference>()?.name == "isDone"
|
||||
}
|
||||
val freeRegister = findFreeRegister(listenableInstructionIndex)
|
||||
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
package app.revanced.patches.youtube.layout.startupshortsreset
|
||||
|
||||
import app.revanced.patcher.StringComparisonType
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.checkCast
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* 20.02+
|
||||
*/
|
||||
internal val BytecodePatchContext.userWasInShortsAlternativeMethod by gettingFirstMethodDeclaratively {
|
||||
internal val userWasInShortsAlternativeMethodMatch = firstMethodComposite {
|
||||
returnType("V")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes("Ljava/lang/Object;")
|
||||
instructions(
|
||||
checkCast("Ljava/lang/Boolean;"),
|
||||
methodCall(smali = "Ljava/lang/Boolean;->booleanValue()Z", after()),
|
||||
allOf(Opcode.CHECK_CAST(), type("Ljava/lang/Boolean;")),
|
||||
after(method { toString() == "Ljava/lang/Boolean;->booleanValue()Z" }),
|
||||
after(Opcode.MOVE_RESULT()),
|
||||
// 20.40+ string was merged into another string and is a partial match.
|
||||
addString("userIsInShorts: ", comparison = StringComparisonType.CONTAINS, afterAtMost(15)),
|
||||
afterAtMost(15, "userIsInShorts: "(String::contains)),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ val themePatch = baseThemePatch(
|
||||
|
||||
block = {
|
||||
val lightThemeBackgroundColor by stringOption(
|
||||
key = "lightThemeBackgroundColor",
|
||||
name = "Light theme background color",
|
||||
default = "@android:color/white",
|
||||
values = mapOf(
|
||||
"White" to "@android:color/white",
|
||||
@@ -46,7 +46,6 @@ val themePatch = baseThemePatch(
|
||||
"Light orange" to "#FFE6CC",
|
||||
"Light red" to "#FFD6D6",
|
||||
),
|
||||
name = "Light theme background color",
|
||||
description = THEME_COLOR_OPTION_DESCRIPTION,
|
||||
)
|
||||
|
||||
|
||||
@@ -56,16 +56,13 @@ val `Remove background playback restrictions` by creatingBytecodePatch(
|
||||
SwitchPreference("revanced_shorts_disable_background_playback"),
|
||||
)
|
||||
|
||||
prefBackgroundAndOfflineCategoryId = getResourceId(
|
||||
ResourceType.STRING,
|
||||
"pref_background_and_offline_category",
|
||||
)
|
||||
prefBackgroundAndOfflineCategoryId = ResourceType.STRING["pref_background_and_offline_category"]
|
||||
|
||||
arrayOf(
|
||||
backgroundPlaybackManagerMethod to "isBackgroundPlaybackAllowed",
|
||||
backgroundPlaybackManagerShortsMethod to "isBackgroundShortsPlaybackAllowed",
|
||||
).forEach { (fingerprint, integrationsMethod) ->
|
||||
fingerprint.method.apply {
|
||||
).forEach { (method, integrationsMethod) ->
|
||||
method.apply {
|
||||
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
@@ -81,7 +78,7 @@ val `Remove background playback restrictions` by creatingBytecodePatch(
|
||||
}
|
||||
|
||||
// Enable background playback option in YouTube settings
|
||||
backgroundPlaybackSettingsMethod.immutableMethod.apply {
|
||||
backgroundPlaybackSettingsMethod.apply {
|
||||
val booleanCalls = instructions.withIndex().filter {
|
||||
it.value.getReference<MethodReference>()?.returnType == "Z"
|
||||
}
|
||||
@@ -101,7 +98,7 @@ val `Remove background playback restrictions` by creatingBytecodePatch(
|
||||
|
||||
// Fix PiP buttons not working after locking/unlocking device screen.
|
||||
if (is_19_34_or_greater) {
|
||||
pipInputConsumerFeatureFlagMethod.let {
|
||||
pipInputConsumerFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it.indices.first(),
|
||||
false,
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
package app.revanced.patches.youtube.misc.backgroundplayback
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.backgroundPlaybackManagerMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.backgroundPlaybackManagerMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returnType("Z")
|
||||
parameterTypes("L")
|
||||
@@ -60,7 +55,7 @@ internal val BytecodePatchContext.backgroundPlaybackSettingsMethod by gettingFir
|
||||
literal { prefBackgroundAndOfflineCategoryId }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.kidsBackgroundPlaybackPolicyControllerMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.kidsBackgroundPlaybackPolicyControllerMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("I", "L", "L")
|
||||
@@ -77,28 +72,24 @@ internal val BytecodePatchContext.kidsBackgroundPlaybackPolicyControllerMethod b
|
||||
literal { 5 }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.backgroundPlaybackManagerShortsMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.backgroundPlaybackManagerShortsMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returnType("Z")
|
||||
parameterTypes("L")
|
||||
instructions(
|
||||
app.revanced.patcher.literal(151635310),
|
||||
)
|
||||
instructions(151635310L())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.shortsBackgroundPlaybackFeatureFlagMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.shortsBackgroundPlaybackFeatureFlagMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
app.revanced.patcher.literal(45415425),
|
||||
)
|
||||
instructions(45415425L())
|
||||
}
|
||||
|
||||
// Fix 'E/InputDispatcher: Window handle pip_input_consumer has no registered input channel'
|
||||
internal val BytecodePatchContext.pipInputConsumerFeatureFlagMethod by gettingFirstMethodDeclaratively {
|
||||
internal val pipInputConsumerFeatureFlagMethodMatch = firstMethodComposite {
|
||||
instructions(
|
||||
// PiP input consumer feature flag.
|
||||
app.revanced.patcher.literal(45638483L),
|
||||
45638483L(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package app.revanced.patches.youtube.misc.dns
|
||||
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patches.shared.misc.dns.checkWatchHistoryDomainNameResolutionPatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod
|
||||
|
||||
val checkWatchHistoryDomainNameResolutionPatch = checkWatchHistoryDomainNameResolutionPatch(
|
||||
block = {
|
||||
@@ -18,5 +20,5 @@ val checkWatchHistoryDomainNameResolutionPatch = checkWatchHistoryDomainNameReso
|
||||
)
|
||||
)
|
||||
},
|
||||
getMainActivityMethod = mainActivityOnCreateFingerprint
|
||||
getMainActivityMethod = BytecodePatchContext::mainActivityOnCreateMethod::get
|
||||
)
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
package app.revanced.patches.youtube.misc.fix.backtoexitgesture
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.after
|
||||
import app.revanced.patcher.checkCast
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.literal
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patcher.*
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@@ -29,11 +20,11 @@ internal val recyclerViewTopScrollingMethodMatch = firstMethodComposite {
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
methodCall(smali = "Ljava/util/Iterator;->next()Ljava/lang/Object;"),
|
||||
method { toString() == "Ljava/util/Iterator;->next()Ljava/lang/Object;" },
|
||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||
checkCast("Landroid/support/v7/widget/RecyclerView;", MatchAfterImmediately()),
|
||||
literal(0, after()),
|
||||
methodCall(definingClass = "Landroid/support/v7/widget/RecyclerView;", after()),
|
||||
after(allOf(Opcode.CHECK_CAST(), type("Landroid/support/v7/widget/RecyclerView;"))),
|
||||
after(0L()),
|
||||
after(method { definingClass == "Landroid/support/v7/widget/RecyclerView;" }),
|
||||
after(Opcode.GOTO()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playbackspeed
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.custom
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
@@ -17,7 +19,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
* This method is usually used to set the initial speed (1.0x) when playback starts from the feed.
|
||||
* For some reason, in the latest YouTube, it is invoked even after the video has already started.
|
||||
*/
|
||||
internal val BytecodePatchContext.playbackSpeedInFeedsMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playbackSpeedInFeedsMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
@@ -31,8 +33,8 @@ internal val BytecodePatchContext.playbackSpeedInFeedsMethod by gettingFirstMeth
|
||||
Opcode.IF_LEZ,
|
||||
Opcode.SUB_LONG_2ADDR,
|
||||
)
|
||||
custom { method, _ ->
|
||||
indexOfGetPlaybackSpeedInstruction(method) >= 0
|
||||
custom {
|
||||
indexOfGetPlaybackSpeedInstruction(this) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,22 @@ package app.revanced.patches.youtube.misc.litho.filter
|
||||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.componentCreateMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.componentCreateMethod by gettingFirstMutableMethodDeclaratively {
|
||||
instructions(
|
||||
"Element missing correct type extension"(),
|
||||
"Element missing type"(),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.lithoFilterMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.lithoFilterMethod by gettingFirstMutableMethodDeclaratively {
|
||||
definingClass { endsWith("/LithoFilterPatch;") }
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.protobufBufferReferenceMethod by gettingFirstMethodDeclaratively {
|
||||
internal val protobufBufferReferenceMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("[B")
|
||||
@@ -38,7 +37,7 @@ internal val BytecodePatchContext.protobufBufferReferenceMethod by gettingFirstM
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("I", "Ljava/nio/ByteBuffer;")
|
||||
@@ -57,27 +56,23 @@ internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDecl
|
||||
custom { immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.lithoThreadExecutorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.lithoThreadExecutorMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameterTypes("I", "I", "I")
|
||||
instructions(1L()) // 1L = default thread timeout.
|
||||
custom {
|
||||
immutableClassDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" &&
|
||||
containsLiteralInstruction(1L) // 1L = default thread timeout.
|
||||
immutableClassDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;"
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.lithoComponentNameUpbFeatureFlagMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.lithoComponentNameUpbFeatureFlagMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
45631264L(),
|
||||
)
|
||||
instructions(45631264L())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.lithoConverterBufferUpbFeatureFlagMethod by gettingFirstMethodDeclaratively {
|
||||
internal val lithoConverterBufferUpbFeatureFlagMethodMatch = firstMethodComposite {
|
||||
returnType("L")
|
||||
instructions(
|
||||
45419603L(),
|
||||
)
|
||||
instructions(45419603L())
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.removeInstructions
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.firstClassDef
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.*
|
||||
@@ -90,7 +91,7 @@ val lithoFilterPatch = bytecodePatch(
|
||||
if (is_20_22_or_greater) {
|
||||
// Hook method that bridges between UPB buffer native code and FB Litho.
|
||||
// Method is found in 19.25+, but is forcefully turned off for 20.21 and lower.
|
||||
protobufBufferReferenceMethod.let {
|
||||
protobufBufferReferenceMethodMatch.let {
|
||||
// Hook the buffer after the call to jniDecode().
|
||||
it.method.addInstruction(
|
||||
it.indices.last() + 1,
|
||||
@@ -112,14 +113,14 @@ val lithoFilterPatch = bytecodePatch(
|
||||
|
||||
// Find the identifier/path fields of the conversion context.
|
||||
|
||||
val conversionContextIdentifierField = conversionContextToStringMethod.method
|
||||
val conversionContextIdentifierField = conversionContextToStringMethod
|
||||
.findFieldFromToString("identifierProperty=")
|
||||
|
||||
val conversionContextPathBuilderField = conversionContextToStringMethod.immutableClassDef
|
||||
.fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
|
||||
|
||||
// Find class and methods to create an empty component.
|
||||
val builderMethodDescriptor = emptyComponentMethod.classDef.methods.single {
|
||||
val builderMethodDescriptor = emptyComponentMethod.immutableClassDef.methods.single {
|
||||
// The only static method in the class.
|
||||
method ->
|
||||
AccessFlags.STATIC.isSet(method.accessFlags)
|
||||
@@ -146,7 +147,7 @@ val lithoFilterPatch = bytecodePatch(
|
||||
|
||||
# 20.41 field is the abstract superclass.
|
||||
# Verify it's the expected subclass just in case.
|
||||
instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.classDef.type}
|
||||
instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef.type}
|
||||
if-eqz v$identifierRegister, :unfiltered
|
||||
|
||||
iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField
|
||||
@@ -196,7 +197,7 @@ val lithoFilterPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// Turn off a feature flag that enables native code of protobuf parsing (Upb protobuf).
|
||||
lithoConverterBufferUpbFeatureFlagMethod.let {
|
||||
lithoConverterBufferUpbFeatureFlagMethodMatch.let {
|
||||
// 20.22 the flag is still enabled in one location, but what it does is not known.
|
||||
// Disable it anyway.
|
||||
it.method.insertLiteralOverride(
|
||||
|
||||
@@ -12,20 +12,23 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.actionBarSearchResultsMethod by gettingFirstMethodDeclaratively {
|
||||
internal val actionBarSearchResultsMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
instructions(
|
||||
ResourceType.LAYOUT("action_bar_search_results_view_mic"),
|
||||
methodCall(name = "setLayoutDirection"),
|
||||
method("setLayoutDirection"),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.toolbarLayoutMethod by gettingFirstMethodDeclaratively {
|
||||
internal val toolbarLayoutMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.ID("toolbar_container"),
|
||||
checkCast("Lcom/google/android/apps/youtube/app/ui/actionbar/MainCollapsingToolbarLayout;"),
|
||||
allOf(
|
||||
Opcode.CHECK_CAST(),
|
||||
type("Lcom/google/android/apps/youtube/app/ui/actionbar/MainCollapsingToolbarLayout;")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,12 +36,10 @@ internal val BytecodePatchContext.toolbarLayoutMethod by gettingFirstMethodDecla
|
||||
* Matches to https://android.googlesource.com/platform/frameworks/support/+/9eee6ba/v7/appcompat/src/android/support/v7/widget/Toolbar.java#963
|
||||
*/
|
||||
internal val BytecodePatchContext.appCompatToolbarBackButtonMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass("Landroid/support/v7/widget/Toolbar;")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/graphics/drawable/Drawable;")
|
||||
parameterTypes()
|
||||
custom { _, classDef ->
|
||||
classDef.type == "Landroid/support/v7/widget/Toolbar;"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,62 +58,57 @@ internal fun ClassDef.getInitializeButtonsMethod() = firstMutableMethodDeclarati
|
||||
* Extension method, used for callback into to other patches.
|
||||
* Specifically, [navigationButtonsPatch].
|
||||
*/
|
||||
internal val BytecodePatchContext.navigationBarHookCallbackMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.navigationBarHookCallbackMethod by gettingFirstMutableMethodDeclaratively {
|
||||
name("navigationTabCreatedCallback")
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("V")
|
||||
parameterTypes(EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR, "Landroid/view/View;")
|
||||
custom { method, _ ->
|
||||
method.name == "navigationTabCreatedCallback" &&
|
||||
method.definingClass == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches to the Enum class that looks up ordinal -> instance.
|
||||
*/
|
||||
internal val BytecodePatchContext.navigationEnumMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.navigationEnumMethod by gettingFirstMethodDeclaratively(
|
||||
"PIVOT_HOME",
|
||||
"TAB_SHORTS",
|
||||
"CREATION_TAB_LARGE",
|
||||
"PIVOT_SUBSCRIPTIONS",
|
||||
"TAB_ACTIVITY",
|
||||
"VIDEO_LIBRARY_WHITE",
|
||||
"INCOGNITO_CIRCLE",
|
||||
"UNKNOWN", // Required to distinguish from patch extension class.
|
||||
) {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
strings(
|
||||
"PIVOT_HOME",
|
||||
"TAB_SHORTS",
|
||||
"CREATION_TAB_LARGE",
|
||||
"PIVOT_SUBSCRIPTIONS",
|
||||
"TAB_ACTIVITY",
|
||||
"VIDEO_LIBRARY_WHITE",
|
||||
"INCOGNITO_CIRCLE",
|
||||
"UNKNOWN", // Required to distinguish from patch extension class.
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.pivotBarButtonsCreateDrawableViewMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
custom { method, _ ->
|
||||
method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" &&
|
||||
// Only one view creation method has a Drawable parameter.
|
||||
method.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;"
|
||||
custom {
|
||||
// Only one view creation method has a Drawable parameter.
|
||||
parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;"
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.pivotBarButtonsCreateResourceStyledViewMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
parameterTypes("L", "Z", "I", "L")
|
||||
custom { method, _ ->
|
||||
method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 20.21+
|
||||
*/
|
||||
internal val BytecodePatchContext.pivotBarButtonsCreateResourceIntViewMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
custom { method, _ ->
|
||||
method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" &&
|
||||
// Only one view creation method has an int first parameter.
|
||||
method.parameterTypes.firstOrNull() == "I"
|
||||
custom {
|
||||
// Only one view creation method has an int first parameter.
|
||||
parameterTypes.firstOrNull() == "I"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +127,7 @@ internal val BytecodePatchContext.pivotBarConstructorMethod by gettingFirstMetho
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.imageEnumConstructorMethod by gettingFirstMethodDeclaratively {
|
||||
internal val imageEnumConstructorMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
"TAB_ACTIVITY_CAIRO"(),
|
||||
@@ -140,13 +136,13 @@ internal val BytecodePatchContext.imageEnumConstructorMethod by gettingFirstMeth
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.setEnumMapMethod by gettingFirstMethodDeclaratively {
|
||||
internal val setEnumMapMethodMatch = firstMethodComposite {
|
||||
instructions(
|
||||
ResourceType.DRAWABLE("yt_fill_bell_black_24"),
|
||||
methodCall(smali = "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;", afterAtMost(10)),
|
||||
methodCall(
|
||||
smali = "Ljava/util/EnumMap;->put(Ljava/lang/Enum;Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
afterAtMost(10),
|
||||
),
|
||||
afterAtMost(10, method { toString() == "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;" }),
|
||||
afterAtMost(
|
||||
10,
|
||||
method { toString() == "Ljava/util/EnumMap;->put(Ljava/lang/Enum;Ljava/lang/Object;)Ljava/lang/Object;" }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
import app.revanced.patcher.extensions.reference
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
@@ -72,7 +73,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
|
||||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$register }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -82,7 +83,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
|
||||
val navigationEnumClassName = navigationEnumMethod.classDef.type
|
||||
addHook(NavigationHook.SET_LAST_APP_NAVIGATION_ENUM) {
|
||||
opcode == Opcode.INVOKE_STATIC &&
|
||||
getReference<MethodReference>()?.definingClass == navigationEnumClassName
|
||||
getReference<MethodReference>()?.definingClass == navigationEnumClassName
|
||||
}
|
||||
|
||||
// Hook the creation of navigation tab views.
|
||||
@@ -120,7 +121,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
|
||||
addInstruction(
|
||||
index + 1,
|
||||
"invoke-static { v$viewRegister, v$isSelectedRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -136,7 +137,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
|
||||
// Two different layouts are used at the hooked code.
|
||||
// Insert before the first ViewGroup method call after inflating,
|
||||
// so this works regardless which layout is used.
|
||||
actionBarSearchResultsMethod.let {
|
||||
actionBarSearchResultsMethodMatch.let {
|
||||
it.method.apply {
|
||||
val instructionIndex = it.indices.last()
|
||||
val viewRegister = getInstruction<FiveRegisterInstruction>(instructionIndex).registerC
|
||||
@@ -144,14 +145,14 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
|
||||
addInstruction(
|
||||
instructionIndex,
|
||||
"invoke-static { v$viewRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Hook the back button visibility.
|
||||
|
||||
toolbarLayoutMethod.let {
|
||||
toolbarLayoutMethodMatch.let {
|
||||
it.method.apply {
|
||||
val index = it.indices.last()
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
@@ -164,43 +165,41 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
|
||||
}
|
||||
|
||||
// Add interface for extensions code to call obfuscated methods.
|
||||
appCompatToolbarBackButtonMethod.let {
|
||||
it.classDef.apply {
|
||||
interfaces.add(EXTENSION_TOOLBAR_INTERFACE)
|
||||
appCompatToolbarBackButtonMethod.classDef.apply {
|
||||
interfaces.add(EXTENSION_TOOLBAR_INTERFACE)
|
||||
|
||||
val definingClass = type
|
||||
val obfuscatedMethodName = it.immutableMethod.name
|
||||
val returnType = "Landroid/graphics/drawable/Drawable;"
|
||||
val definingClass = type
|
||||
val obfuscatedMethodName = appCompatToolbarBackButtonMethod.name
|
||||
val returnType = "Landroid/graphics/drawable/Drawable;"
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
definingClass,
|
||||
"patch_getNavigationIcon",
|
||||
listOf(),
|
||||
returnType,
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
definingClass,
|
||||
"patch_getNavigationIcon",
|
||||
listOf(),
|
||||
returnType,
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-virtual { p0 }, $definingClass->$obfuscatedMethodName()$returnType
|
||||
move-result-object v0
|
||||
return-object v0
|
||||
""",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
hookNavigationButtonCreated = { extensionClassDescriptor ->
|
||||
navigationBarHookCallbackMethod.addInstruction(
|
||||
0,
|
||||
"invoke-static { p0, p1 }, $extensionClassDescriptor->navigationTabCreated" +
|
||||
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
|
||||
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -212,22 +211,24 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
|
||||
|
||||
// Fix YT bug of notification tab missing the filled icon.
|
||||
if (is_19_35_or_greater && !is_20_39_or_greater) { // FIXME: 20.39+ needs this fix.
|
||||
val cairoNotificationEnumReference = imageEnumConstructorMethod
|
||||
.instructionMatches.last().getInstruction<ReferenceInstruction>().reference
|
||||
val cairoNotificationEnumReference =
|
||||
imageEnumConstructorMethodMatch.method.getInstruction(imageEnumConstructorMethodMatch.indices.last()).reference
|
||||
|
||||
setEnumMapMethod.apply {
|
||||
val setEnumIntegerIndex = setEnumMapMethod.indices.last()
|
||||
val enumMapRegister = getInstruction<FiveRegisterInstruction>(setEnumIntegerIndex).registerC
|
||||
val insertIndex = setEnumIntegerIndex + 1
|
||||
val freeRegister = findFreeRegister(insertIndex, enumMapRegister)
|
||||
setEnumMapMethodMatch.apply {
|
||||
val setEnumIntegerIndex = setEnumMapMethodMatch.indices.last()
|
||||
method.apply {
|
||||
val enumMapRegister = getInstruction<FiveRegisterInstruction>(setEnumIntegerIndex).registerC
|
||||
val insertIndex = setEnumIntegerIndex + 1
|
||||
val freeRegister = findFreeRegister(insertIndex, enumMapRegister)
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
sget-object v$freeRegister, $cairoNotificationEnumReference
|
||||
invoke-static { v$enumMapRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V
|
||||
""",
|
||||
)
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
sget-object v$freeRegister, $cairoNotificationEnumReference
|
||||
invoke-static { v$enumMapRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.playerControlsVisibilityEntityModelMethod by gettingFirstMethodDeclaratively {
|
||||
internal val playerControlsVisibilityEntityModelMethodMatch = firstMethodComposite {
|
||||
name("getPlayerControlsVisibility")
|
||||
accessFlags(AccessFlags.PUBLIC)
|
||||
returnType("L")
|
||||
|
||||
@@ -16,7 +16,7 @@ val playerControlsOverlayVisibilityPatch = bytecodePatch {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
apply {
|
||||
playerControlsVisibilityEntityModelMethod.let {
|
||||
playerControlsVisibilityEntityModelMethodMatch.let {
|
||||
it.method.apply {
|
||||
val startIndex = it.indices.first()
|
||||
val iGetReference = getInstruction<ReferenceInstruction>(startIndex).reference
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package app.revanced.patches.youtube.misc.recyclerviewtree.hook
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.opcodes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.recyclerViewTreeObserverMethod by gettingFirstMethodDeclaratively {
|
||||
internal val recyclerViewTreeObserverMethodMatch = firstMethodComposite("LithoRVSLCBinder") {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
opcodes(
|
||||
Opcode.CHECK_CAST,
|
||||
@@ -16,5 +15,4 @@ internal val BytecodePatchContext.recyclerViewTreeObserverMethod by gettingFirst
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.NEW_INSTANCE,
|
||||
)
|
||||
strings("LithoRVSLCBinder")
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ val recyclerViewTreeHookPatch = bytecodePatch {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
apply {
|
||||
recyclerViewTreeObserverMethod.apply {
|
||||
val insertIndex = recyclerViewTreeObserverMethod.indices.first() + 1
|
||||
recyclerViewTreeObserverMethodMatch.let {
|
||||
val insertIndex = it.indices.first() + 1
|
||||
val recyclerViewParameter = 2
|
||||
|
||||
addRecyclerViewTreeHook = { classDescriptor ->
|
||||
addInstruction(
|
||||
it.method.addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static/range { p$recyclerViewParameter .. p$recyclerViewParameter }, " +
|
||||
"$classDescriptor->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.youtube.misc.spoof
|
||||
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||
@@ -13,10 +14,11 @@ import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod
|
||||
|
||||
val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
|
||||
extensionClassDescriptor = "Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;",
|
||||
getMainActivityOnCreateMethod = mainActivityOnCreateFingerprint,
|
||||
getMainActivityOnCreateMethod = BytecodePatchContext::mainActivityOnCreateMethod::get,
|
||||
fixMediaFetchHotConfig = {
|
||||
is_19_34_or_greater
|
||||
},
|
||||
|
||||
@@ -78,7 +78,7 @@ internal val BytecodePatchContext.mainActivityOnCreateMethod by gettingFirstMuta
|
||||
parameterTypes("Landroid/os/Bundle;")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.rollingNumberTextViewAnimationUpdateMethod by gettingFirstMethodDeclaratively {
|
||||
internal val rollingNumberTextViewAnimationUpdateMethodMatch = firstMethodComposite {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("Landroid/graphics/Bitmap;")
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package app.revanced.patches.youtube.video.audio
|
||||
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patches.shared.misc.audio.forceOriginalAudioPatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateMethod
|
||||
|
||||
@Suppress("unused")
|
||||
val forceOriginalAudioPatch = forceOriginalAudioPatch(
|
||||
@@ -26,7 +28,7 @@ val forceOriginalAudioPatch = forceOriginalAudioPatch(
|
||||
)
|
||||
},
|
||||
fixUseLocalizedAudioTrackFlag = { is_20_07_or_greater },
|
||||
getMainActivityOnCreateMethod = mainActivityOnCreateFingerprint,
|
||||
getMainActivityOnCreateMethod = BytecodePatchContext::mainActivityOnCreateMethod::get,
|
||||
subclassExtensionClassDescriptor = "Lapp/revanced/extension/youtube/patches/ForceOriginalAudioPatch;",
|
||||
preferenceScreen = PreferenceScreen.VIDEO,
|
||||
)
|
||||
|
||||
@@ -2,15 +2,15 @@ package app.revanced.patches.youtube.video.codecs
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val BytecodePatchContext.vp9CapabilityMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.vp9CapabilityMethod by gettingFirstMutableMethodDeclaratively(
|
||||
"vp9_supported",
|
||||
"video/x-vnd.on2.vp9",
|
||||
) {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
strings(
|
||||
"vp9_supported",
|
||||
"video/x-vnd.on2.vp9",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
/**
|
||||
* For targets 20.46 and later.
|
||||
*/
|
||||
internal val BytecodePatchContext.playerParameterBuilderMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playerParameterBuilderMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes(
|
||||
@@ -33,7 +33,7 @@ internal val BytecodePatchContext.playerParameterBuilderMethod by gettingFirstMe
|
||||
/**
|
||||
* For targets 20.26 and later.
|
||||
*/
|
||||
internal val BytecodePatchContext.playerParameterBuilder2026Method by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playerParameterBuilder2026Method by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes(
|
||||
@@ -60,7 +60,7 @@ internal val BytecodePatchContext.playerParameterBuilder2026Method by gettingFir
|
||||
/**
|
||||
* For targets 20.15 to 20.25
|
||||
*/
|
||||
internal val BytecodePatchContext.playerParameterBuilder2015Method by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playerParameterBuilder2015Method by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes(
|
||||
@@ -86,7 +86,7 @@ internal val BytecodePatchContext.playerParameterBuilder2015Method by gettingFir
|
||||
/**
|
||||
* For targets 20.10 to 20.14.
|
||||
*/
|
||||
internal val BytecodePatchContext.playerParameterBuilder2010Method by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playerParameterBuilder2010Method by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes(
|
||||
@@ -113,7 +113,7 @@ internal val BytecodePatchContext.playerParameterBuilder2010Method by gettingFir
|
||||
/**
|
||||
* For targets 20.02 to 20.09.
|
||||
*/
|
||||
internal val BytecodePatchContext.playerParameterBuilder2002Method by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playerParameterBuilder2002Method by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes(
|
||||
@@ -139,7 +139,7 @@ internal val BytecodePatchContext.playerParameterBuilder2002Method by gettingFir
|
||||
/**
|
||||
* For targets 19.25 to 19.50.
|
||||
*/
|
||||
internal val BytecodePatchContext.playerParameterBuilder1925Method by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playerParameterBuilder1925Method by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes(
|
||||
@@ -164,7 +164,7 @@ internal val BytecodePatchContext.playerParameterBuilder1925Method by gettingFir
|
||||
/**
|
||||
* For targets 19.01 to 19.24.
|
||||
*/
|
||||
internal val BytecodePatchContext.playerParameterBuilderLegacyMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.playerParameterBuilderLegacyMethod by gettingFirstMutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("L")
|
||||
parameterTypes(
|
||||
|
||||
@@ -40,30 +40,30 @@ val playerResponseMethodHookPatch = bytecodePatch {
|
||||
)
|
||||
|
||||
apply {
|
||||
val fingerprint: Fingerprint
|
||||
val method: MutableMethod
|
||||
if (is_20_46_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 13
|
||||
fingerprint = playerParameterBuilderMethod
|
||||
method = playerParameterBuilderMethod
|
||||
} else if (is_20_26_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 13
|
||||
fingerprint = playerParameterBuilder2026Method
|
||||
method = playerParameterBuilder2026Method
|
||||
} else if (is_20_15_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 13
|
||||
fingerprint = playerParameterBuilder2015Method
|
||||
method = playerParameterBuilder2015Method
|
||||
} else if (is_20_10_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 13
|
||||
fingerprint = playerParameterBuilder2010Method
|
||||
method = playerParameterBuilder2010Method
|
||||
} else if (is_20_02_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 12
|
||||
fingerprint = playerParameterBuilder2002Method
|
||||
method = playerParameterBuilder2002Method
|
||||
} else if (is_19_23_or_greater) {
|
||||
parameterIsShortAndOpeningOrPlaying = 12
|
||||
fingerprint = playerParameterBuilder1925Method
|
||||
method = playerParameterBuilder1925Method
|
||||
} else {
|
||||
parameterIsShortAndOpeningOrPlaying = 11
|
||||
fingerprint = playerParameterBuilderLegacyMethod
|
||||
method = playerParameterBuilderLegacyMethod
|
||||
}
|
||||
playerResponseMethod = fingerprint.method
|
||||
playerResponseMethod = method
|
||||
|
||||
// On some app targets the method has too many registers pushing the parameters past v15.
|
||||
// If needed, move the parameters to 4-bit registers, so they can be passed to the extension.
|
||||
|
||||
@@ -18,7 +18,7 @@ import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeH
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
internal var videoQualityBottomSheetListFragmentname = -1L
|
||||
internal var videoQualityBottomSheetListFragmentTitle = -1L
|
||||
private set
|
||||
internal var videoQualityQuickMenuAdvancedMenuDescription = -1L
|
||||
private set
|
||||
@@ -47,7 +47,7 @@ internal val advancedVideoQualityMenuPatch = bytecodePatch {
|
||||
)
|
||||
|
||||
// Used for the old type of the video quality menu.
|
||||
videoQualityBottomSheetListFragmentname =
|
||||
videoQualityBottomSheetListFragmentTitle =
|
||||
ResourceType.LAYOUT["video_quality_bottom_sheet_list_fragment_title"]
|
||||
videoQualityQuickMenuAdvancedMenuDescription =
|
||||
ResourceType.STRING["video_quality_quick_menu_advanced_menu_description"]
|
||||
|
||||
Reference in New Issue
Block a user