some more migrations & fixing = to by for creating patches

This commit is contained in:
oSumAtrIX
2026-01-25 01:10:30 +01:00
parent 230ab2fa59
commit 56eff2a625
26 changed files with 156 additions and 195 deletions

View File

@@ -10,7 +10,7 @@ import org.w3c.dom.Element
import java.io.File
@Suppress("unused", "ObjectPropertyName")
val `Custom network security` = creatingResourcePatch(
val `Custom network security` by creatingResourcePatch(
description = "Allows trusting custom certificate authorities for a specific domain.",
use = false,
) {

View File

@@ -6,7 +6,7 @@ import app.revanced.util.asSequence
import app.revanced.util.getNode
@Suppress("unused", "ObjectPropertyName")
val `Export internal data documents provider` = creatingResourcePatch(
val `Export internal data documents provider` by creatingResourcePatch(
description = "Exports a documents provider that grants access to the internal data directory of this app " +
"to file managers and other apps that support the Storage Access Framework.",
use = false,

View File

@@ -8,7 +8,7 @@ import app.revanced.patches.shared.misc.hex.hexPatch
import app.revanced.util.Utils.trimIndentMultiline
@Suppress("unused")
val Hex = creatingRawResourcePatch(
val Hex by creatingRawResourcePatch(
description = "Replaces a hexadecimal patterns of bytes of files in an APK.",
use = false,
) {
@@ -40,13 +40,13 @@ val Hex = creatingRawResourcePatch(
} catch (e: Exception) {
throw PatchException(
"Invalid replacement: $replacement.\n" +
"Every pattern must be followed by a pipe ('|'), " +
"the replacement pattern, another pipe ('|'), " +
"and the path to the file to make the changes in relative to the APK root. ",
"Every pattern must be followed by a pipe ('|'), " +
"the replacement pattern, another pipe ('|'), " +
"and the path to the file to make the changes in relative to the APK root. ",
)
}
}
},
)
),
)
}

View File

@@ -27,7 +27,7 @@ fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
}
@Suppress("ObjectPropertyName")
val `Change package name` = creatingResourcePatch(
val `Change package name` by creatingResourcePatch(
description = "Appends \".revanced\" to the package name by default. " +
"Changing the package name of the app can lead to unexpected issues.",
use = false,

View File

@@ -8,7 +8,7 @@ import java.io.FileNotFoundException
import java.util.logging.Logger
@Suppress("unused", "ObjectPropertyName")
val `Remove share targets` = creatingResourcePatch(
val `Remove share targets` by creatingResourcePatch(
description = "Removes share targets like directly sharing to a frequent contact.",
use = false,
) {

View File

@@ -14,7 +14,7 @@ import java.security.cert.CertificateFactory
import java.util.*
@Suppress("unused", "ObjectPropertyName")
val `Enable ROM signature spoofing` = creatingResourcePatch(
val `Enable ROM signature spoofing` by creatingResourcePatch(
description = "Spoofs the signature via the manifest meta-data \"fake-signature\". " +
"This patch only works with ROMs that support signature spoofing.",
use = false,

View File

@@ -6,7 +6,7 @@ import org.w3c.dom.Element
import java.util.logging.Logger
@Suppress("unused", "ObjectPropertyName")
val `Set target SDK version 34` = creatingResourcePatch(
val `Set target SDK version 34` by creatingResourcePatch(
description = "Changes the target SDK to version 34 (Android 14). " +
"For devices running Android 15+, this will disable edge-to-edge display.",
use = false,

View File

@@ -6,7 +6,7 @@ import app.revanced.util.getNode
import org.w3c.dom.Element
@Suppress("unused", "ObjectPropertyName")
val `Change version code` = creatingResourcePatch(
val `Change version code` by creatingResourcePatch(
description = "Changes the version code of the app. This will turn off app store updates " +
"and allows downgrading an existing app install to an older app version.",
use = false,

View File

@@ -1,10 +1,12 @@
package app.revanced.patches.reddit.customclients.sync.ads
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
fun `Disable ads`(block: BytecodePatchBuilder.() -> Unit = {}) = creatingBytecodePatch {
fun disableAdsPatch(block: BytecodePatchBuilder.() -> Unit = {}) = bytecodePatch(
name = "Disable ads",
) {
apply {
isAdsEnabledMethod.returnEarly(false)
}

View File

@@ -1,10 +1,10 @@
package app.revanced.patches.reddit.customclients.sync.syncforlemmy.ads
import app.revanced.patches.reddit.customclients.sync.ads.`Disable ads`
import app.revanced.patches.reddit.customclients.sync.ads.disableAdsPatch
import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch
@Suppress("unused")
val disableAdsPatch = `Disable ads` {
val disableAdsPatch = disableAdsPatch {
dependsOn(disablePiracyDetectionPatch)
compatibleWith("com.laurencedawson.reddit_sync")

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.reddit.customclients.sync.syncforreddit.ads
import app.revanced.patches.reddit.customclients.sync.ads.`Disable ads`
import app.revanced.patches.reddit.customclients.sync.ads.disableAdsPatch
@Suppress("unused")
val disableAdsPatch = `Disable ads` {
val disableAdsPatch = disableAdsPatch {
compatibleWith("io.syncapps.lemmy_sync")
}

View File

@@ -27,7 +27,7 @@ val `Use /user/ endpoint` by creatingBytecodePatch(
oAuthUserIdRequestMethodMatch,
oAuthUserInfoRequestMethodMatch,
).map { match ->
match.stringIndices.values.first() to match.method
match.indices.first() to match.method
}.forEach { (userPathStringIndex, method) ->
val userPathStringInstruction = method.getInstruction<OneRegisterInstruction>(userPathStringIndex)

View File

@@ -1,50 +1,39 @@
package app.revanced.patches.shared.layout.theme
import app.revanced.patcher.accessFlags
import app.revanced.patcher.allOf
import app.revanced.patcher.field
import app.revanced.patcher.fieldAccess
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.methodCall
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.lithoOnBoundsChangeMethod by gettingFirstMethodDeclaratively {
internal val lithoOnBoundsChangeMethodMatch = firstMethodComposite {
name("onBoundsChange")
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
returnType("V")
parameterTypes("Landroid/graphics/Rect;")
lateinit var methodDefiningClass: String
custom {
methodDefiningClass = definingClass
true
}
instructions(
allOf(Opcode.IPUT_OBJECT(), field { definingClass type == "Landroid/graphics/Path;" }),
fieldAccess(
opcode = Opcode.IPUT_OBJECT,
definingClass = "this",
type = "Landroid/graphics/Path;",
allOf(
Opcode.IPUT_OBJECT(),
field { type == "Landroid/graphics/Path;" && definingClass == methodDefiningClass },
),
methodCall(
definingClass = "this",
name = "isStateful",
returnType = "Z",
afterAtMost(5),
afterAtMost(
5,
method { returnType == "Z" && name == "isStateful" && definingClass == methodDefiningClass },
),
fieldAccess(
opcode = Opcode.IGET_OBJECT,
definingClass = "this",
type = "Landroid/graphics/Paint",
afterAtMost(5),
afterAtMost(
5,
allOf(
Opcode.IGET_OBJECT(),
field { type == "Landroid/graphics/Path;" && definingClass == methodDefiningClass },
),
),
methodCall(
smali = "Landroid/graphics/Paint;->setColor(I)V",
after(),
after(
method { toString() == "Landroid/graphics/Paint;->setColor(I)V" },
),
)
custom { method, _ ->
method.name == "onBoundsChange"
}
}

View File

@@ -11,10 +11,10 @@ val lithoColorHookPatch = bytecodePatch(
) {
apply {
var insertionIndex = lithoOnBoundsChangeMethod.patternMatch.endIndex - 1
var insertionIndex = lithoOnBoundsChangeMethodMatch.indices.last() - 1
lithoColorOverrideHook = { targetMethodClass, targetMethodName ->
lithoOnBoundsChangeMethod.addInstructions(
lithoOnBoundsChangeMethodMatch.method.addInstructions(
insertionIndex,
"""
invoke-static { p1 }, $targetMethodClass->$targetMethodName(I)I

View File

@@ -6,7 +6,7 @@ import app.revanced.util.getNode
import org.w3c.dom.Element
@Suppress("unused", "ObjectPropertyName")
val `Disable Sentry telemetry` = creatingResourcePatch(
val `Disable Sentry telemetry` by creatingResourcePatch(
description = "Disables Sentry telemetry. See https://sentry.io/for/android/ for more information.",
use = false,
) {

View File

@@ -1,53 +1,29 @@
package app.revanced.patches.songpal.badge
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
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.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
// Located @ ub.i0.h#p (9.5.0)
internal val BytecodePatchContext.createTabsMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.createTabsMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PRIVATE)
returnType("Ljava/util/List;")
custom { method, _ ->
method.implementation?.instructions?.any { instruction ->
if (instruction.opcode != Opcode.INVOKE_STATIC) return@any false
val reference = (instruction as ReferenceInstruction).reference as MethodReference
if (reference.parameterTypes.isNotEmpty()) return@any false
if (reference.definingClass != ACTIVITY_TAB_DESCRIPTOR) return@any false
if (reference.returnType != "[${ACTIVITY_TAB_DESCRIPTOR}") return@any false
true
} == true
}
instructions(
method {
parameterTypes.isEmpty() &&
definingClass == ACTIVITY_TAB_DESCRIPTOR &&
returnType == "[${ACTIVITY_TAB_DESCRIPTOR}"
},
)
}
// Located @ com.sony.songpal.mdr.vim.activity.MdrRemoteBaseActivity.e#run (9.5.0)
internal val BytecodePatchContext.showNotificationMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.showNotificationMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC)
returnType("V")
custom { method, _ ->
method.implementation?.instructions?.any { instruction ->
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@any false
with(expectedReference) {
val currentReference = (instruction as ReferenceInstruction).reference as MethodReference
currentReference.let {
if (it.definingClass != definingClass) return@any false
if (it.parameterTypes != parameterTypes) return@any false
if (it.returnType != returnType) return@any false
}
}
true
} == true
}
instructions(method { MethodUtil.methodSignaturesMatch(this, expectedReference) })
}
internal val expectedReference = ImmutableMethodReference(

View File

@@ -1,5 +1,13 @@
package app.revanced.patches.viber.ads
internal val BytecodePatchContext.findAdStringMethod by gettingFirstMethodDeclaratively {
strings("viber_plus_debug_ads_free_flag")
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import com.android.tools.smali.dexlib2.Opcode
internal val findAdStringMethodMatch = firstMethodComposite {
instructions(
Opcode.NEW_INSTANCE(),
"viber_plus_debug_ads_free_flag"(),
)
}

View File

@@ -1,12 +1,14 @@
package app.revanced.patches.viber.ads
import app.revanced.patcher.definingClass
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.typeReference
import app.revanced.patcher.firstMutableMethodDeclaratively
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.patcher.returnType
import app.revanced.util.returnEarly
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
@Suppress("unused", "ObjectPropertyName")
val `Hide Ads` by creatingBytecodePatch(
@@ -15,26 +17,15 @@ val `Hide Ads` by creatingBytecodePatch(
compatibleWith("com.viber.voip"("25.9.2.0", "26.1.2.0"))
apply {
val method = findAdStringMethod
val referenceIndex = findAdStringMethodMatch.indices.first()
// Find the ads free string index
val stringIndex = findAdStringMethod.stringMatches.first().index
val targetClass =
findAdStringMethodMatch.immutableMethod.getInstruction<ReferenceInstruction>(referenceIndex).typeReference
// Search backwards from the string to find the `new-instance` (TypeReference) instruction
val typeRefIndex =
method.indexOfFirstInstructionReversedOrThrow(stringIndex) { this.opcode == Opcode.NEW_INSTANCE }
// Get the class name from the TypeReference
val targetClass = method.getInstruction<ReferenceInstruction>(typeRefIndex).reference as TypeReference
// Patch the ads-free method to always return true
val adFreeFingerprint = fingerprint {
val adFreeFingerprint = firstMutableMethodDeclaratively {
definingClass(targetClass!!.type)
returnType("I")
parameterTypes()
custom { method, classDef ->
classDef == targetClass
}
}
adFreeFingerprint.method.returnEarly(1)
}.returnEarly(1)
}
}

View File

@@ -17,7 +17,7 @@ import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
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.conversionContextFingerprintToString
import app.revanced.patches.youtube.shared.conversionContextToStringMethod
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateMethod
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
import app.revanced.patches.youtube.video.videoid.hookVideoId
@@ -121,7 +121,7 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
// This hook handles all situations, as it's where the created Spans are stored and later reused.
// Find the field name of the conversion context.
val conversionContextClass = conversionContextFingerprintToString.originalClassDef
val conversionContextClass = conversionContextToStringMethod.originalClassDef
val textComponentConversionContextField = textComponentConstructorMethod.originalClassDef.fields.find {
it.type == conversionContextClass.type ||
// 20.41+ uses superclass field type.

View File

@@ -4,10 +4,13 @@ import app.revanced.patcher.accessFlags
import app.revanced.patcher.addString
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.methodCall
import app.revanced.patcher.opcode
import app.revanced.patcher.opcodes
@@ -113,15 +116,18 @@ internal val BytecodePatchContext.playerLinearGradientLegacyMethod by gettingFir
internal const val LOTTIE_ANIMATION_VIEW_CLASS_TYPE = "Lcom/airbnb/lottie/LottieAnimationView;"
internal val BytecodePatchContext.lottieAnimationViewSetAnimationIntMethod by gettingFirstMethodDeclaratively {
definingClass(LOTTIE_ANIMATION_VIEW_CLASS_TYPE)
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("I")
returnType("V")
instructions(
methodCall("this", "isInEditMode"),
)
custom { _, classDef ->
classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE
lateinit var methodDefiningClass: String
custom {
methodDefiningClass = definingClass
true
}
instructions(method { name == "isInEditMode" && definingClass == methodDefiningClass })
}
internal val BytecodePatchContext.lottieCompositionFactoryZipMethod by gettingFirstMethodDeclaratively {

View File

@@ -3,20 +3,18 @@ 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.gettingFirstMethodDeclaratively
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.literal
import app.revanced.patcher.methodCall
import app.revanced.patcher.opcode
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
internal val BytecodePatchContext.scrollPositionMethod by gettingFirstMethodDeclaratively {
internal val scrollPositionMethodMatch = firstMethodComposite("scroll_position") {
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
returnType("V")
parameterTypes("L")
@@ -25,10 +23,9 @@ internal val BytecodePatchContext.scrollPositionMethod by gettingFirstMethodDecl
Opcode.INVOKE_DIRECT,
Opcode.RETURN_VOID,
)
strings("scroll_position")
}
internal val BytecodePatchContext.recyclerViewTopScrollingMethod by gettingFirstMethodDeclaratively {
internal val recyclerViewTopScrollingMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes()

View File

@@ -16,27 +16,25 @@ internal val fixBackToExitGesturePatch = bytecodePatch(
) {
apply {
recyclerViewTopScrollingMethod.let {
it.method.addInstructionsAtControlFlowLabel(
it.indices.last() + 1,
with(recyclerViewTopScrollingMethodMatch) {
method.addInstructionsAtControlFlowLabel(
indices.last() + 1,
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onTopView()V",
)
}
scrollPositionMethod.let {
navigate(it.originalMethod)
.to(it.patternMatch.startIndex + 1)
.stop().apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.definingClass ==
"Landroid/support/v7/widget/RecyclerView;"
}
addInstruction(
index,
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onScrollingViews()V",
)
with(scrollPositionMethodMatch) {
navigate(immutableMethod).to(indices.first() + 1).stop().apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.definingClass ==
"Landroid/support/v7/widget/RecyclerView;"
}
addInstruction(
index,
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onScrollingViews()V",
)
}
}
mainActivityOnBackPressedMethod.apply {

View File

@@ -10,7 +10,7 @@ import app.revanced.patcher.firstClassDef
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.*
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
import app.revanced.patches.youtube.shared.conversionContextToStringMethod
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -112,10 +112,10 @@ val lithoFilterPatch = bytecodePatch(
// Find the identifier/path fields of the conversion context.
val conversionContextIdentifierField = conversionContextFingerprintToString.method
val conversionContextIdentifierField = conversionContextToStringMethod.method
.findFieldFromToString("identifierProperty=")
val conversionContextPathBuilderField = conversionContextFingerprintToString.originalClassDef
val conversionContextPathBuilderField = conversionContextToStringMethod.originalClassDef
.fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
// Find class and methods to create an empty component.
@@ -146,7 +146,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, ${conversionContextFingerprintToString.classDef.type}
instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.classDef.type}
if-eqz v$identifierRegister, :unfiltered
iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField

View File

@@ -10,6 +10,7 @@ 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.actionBarSearchResultsMethod by gettingFirstMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
@@ -43,7 +44,8 @@ internal val BytecodePatchContext.appCompatToolbarBackButtonMethod by gettingFir
/**
* Matches to the class found in [pivotBarConstructorMethod].
*/
internal val BytecodePatchContext.initializeButtonsMethod by gettingFirstMethodDeclaratively {
context(_: BytecodePatchContext)
internal fun ClassDef.getInitializeButtonsMethod() = firstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
instructions(
@@ -114,16 +116,12 @@ internal val BytecodePatchContext.pivotBarButtonsCreateResourceIntViewMethod by
}
}
internal val BytecodePatchContext.pivotBarButtonsViewSetSelectedMethod by gettingFirstMethodDeclaratively {
internal val pivotBarButtonsViewSetSelectedMethodMatch = firstMethodComposite {
definingClass("Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("I", "Z")
instructions(
methodCall(name = "setSelected"),
)
custom { method, _ ->
method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;"
}
instructions(method("setSelected"))
}
internal val BytecodePatchContext.pivotBarConstructorMethod by gettingFirstMethodDeclaratively {

View File

@@ -1,9 +1,11 @@
package app.revanced.patches.youtube.misc.navigation
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.instructions
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
@@ -75,7 +77,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
}
}
initializeButtonsMethod.match(pivotBarConstructorMethod.originalClassDef).method.apply {
pivotBarConstructorMethod.immutableClassDef.getInitializeButtonsMethod().apply {
// Hook the current navigation bar enum value. Note, the 'You' tab does not have an enum value.
val navigationEnumClassName = navigationEnumMethod.classDef.type
addHook(NavigationHook.SET_LAST_APP_NAVIGATION_ENUM) {
@@ -93,37 +95,33 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
}
if (is_20_21_or_greater && !is_20_28_or_greater) {
val imageResourceIntTabMethod = pivotBarButtonsCreateResourceIntViewMethod.originalMethod
addHook(NavigationHook.NAVIGATION_TAB_LOADED) predicate@{
MethodUtil.methodSignaturesMatch(
getReference<MethodReference>() ?: return@predicate false,
imageResourceIntTabMethod,
pivotBarButtonsCreateResourceIntViewMethod,
)
}
}
val imageResourceTabMethod = pivotBarButtonsCreateResourceStyledViewMethod.originalMethod
addHook(NavigationHook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{
MethodUtil.methodSignaturesMatch(
getReference<MethodReference>() ?: return@predicate false,
imageResourceTabMethod,
pivotBarButtonsCreateResourceStyledViewMethod,
)
}
}
pivotBarButtonsViewSetSelectedMethod.let {
it.method.apply {
val index = it.instructionMatches.first().index
val instruction = getInstruction<FiveRegisterInstruction>(index)
val viewRegister = instruction.registerC
val isSelectedRegister = instruction.registerD
pivotBarButtonsViewSetSelectedMethodMatch.method.apply {
val index = pivotBarButtonsViewSetSelectedMethodMatch.indices.first()
val instruction = getInstruction<FiveRegisterInstruction>(index)
val viewRegister = instruction.registerC
val isSelectedRegister = instruction.registerD
addInstruction(
index + 1,
"invoke-static { v$viewRegister, v$isSelectedRegister }, " +
"$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V",
)
}
addInstruction(
index + 1,
"invoke-static { v$viewRegister, v$isSelectedRegister }, " +
"$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V",
)
}
// Hook onto back button pressed. Needed to fix race problem with

View File

@@ -3,11 +3,15 @@ package app.revanced.patches.youtube.shared
import app.revanced.patcher.accessFlags
import app.revanced.patcher.after
import app.revanced.patcher.allOf
import app.revanced.patcher.custom
import app.revanced.patcher.definingClass
import app.revanced.patcher.field
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.firstMethodDeclaratively
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.instruction
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.method
@@ -23,19 +27,16 @@ import com.android.tools.smali.dexlib2.Opcode
internal const val YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE = "Lcom/google/android/apps/youtube/app/watchwhile/MainActivity;"
internal val conversionContextFingerprintToString = fingerprint {
internal val BytecodePatchContext.conversionContextToStringMethod by gettingFirstMethodDeclaratively(
", widthConstraint=",
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
", identifierProperty=",
) {
name("toString")
parameterTypes()
strings(
"ConversionContext{", // Partial string match.
", widthConstraint=",
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
", identifierProperty=",
)
custom { method, _ ->
method.name == "toString"
}
instructions("ConversionContext{"(String::startsWith)) // Partial string match.
}
internal fun getLayoutConstructorMethodMatch() = firstMethodComposite {
@@ -57,20 +58,17 @@ internal fun getLayoutConstructorMethodMatch() = firstMethodComposite {
}
internal val BytecodePatchContext.mainActivityConstructorMethod by gettingFirstMethodDeclaratively {
definingClass(YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE)
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameterTypes()
custom { _, classDef ->
classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
}
}
internal val BytecodePatchContext.mainActivityOnBackPressedMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.mainActivityOnBackPressedMethod by gettingFirstMutableMethodDeclaratively {
name("onBackPressed")
definingClass(YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE)
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes()
custom { method, classDef ->
method.name == "onBackPressed" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
}
}
internal val BytecodePatchContext.mainActivityOnCreateMethod by gettingFirstMutableMethodDeclaratively {
@@ -99,9 +97,9 @@ internal val BytecodePatchContext.rollingNumberTextViewAnimationUpdateMethod by
Opcode.INT_TO_FLOAT,
Opcode.INVOKE_VIRTUAL, // set textview padding using bitmap width
)
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;"
}
}