This commit is contained in:
oSumAtrIX
2026-01-28 13:42:56 +01:00
parent ac5b65fcb5
commit 43f919d9ad
31 changed files with 218 additions and 273 deletions

View File

@@ -1,8 +1,6 @@
package app.revanced.patches.spotify.misc.extension
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.*
internal val loadOrbitLibraryMethodMatch = firstMethodComposite {
instructions(

View File

@@ -1,30 +1,52 @@
package app.revanced.patches.spotify.misc.extension
import app.revanced.patcher.definingClass
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.name
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.returnType
import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook
import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.patches.spotify.shared.mainActivityOnCreateMethod
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal val mainActivityOnCreateHook = extensionHook { mainActivityOnCreateMethod }
internal val mainActivityOnCreateHook = activityOnCreateExtensionHook(
"Lcom/spotify/music/SpotifyMainActivity;"
)
internal val loadOrbitLibraryHook = extensionHook {
// FIXME: Creating this is a mess and needs refactoring.
extensionHook(
getInsertIndex = {
loadOrbitLibraryMethodMatch.stringMatches.last().index
},
getContextRegister = { method ->
val contextReferenceIndex = method.indexOfFirstInstruction {
getReference<FieldReference>()?.type == "Landroid/content/Context;"
private var contextReferenceIndex = -1
internal val loadOrbitLibraryHook = extensionHook(
getInsertIndex = {
// Find the last orbit_library_load string usage
var lastIndex = -1
instructions.forEachIndexed { index, instruction ->
instruction.toString().let {
if (it.contains("orbit_library_load") || it.contains("orbit-jni-spotify")) {
lastIndex = index
}
}
val contextRegister =
method.getInstruction<TwoRegisterInstruction>(contextReferenceIndex).registerA
}
lastIndex
},
getContextRegister = {
contextReferenceIndex = indexOfFirstInstruction {
getReference<FieldReference>()?.type == "Landroid/content/Context;"
}
val contextRegister =
getInstruction<TwoRegisterInstruction>(contextReferenceIndex).registerA
"v$contextRegister"
},
fingerprint = loadOrbitLibraryMethodMatch,
"v$contextRegister"
},
) {
instructions(
"orbit_library_load"(),
"orbit-jni-spotify"()
)
}

View File

@@ -1,34 +1,30 @@
package app.revanced.patches.spotify.misc.privacy
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.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val BytecodePatchContext.shareCopyUrlMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.shareCopyUrlMethod by gettingFirstMutableMethodDeclarativelyOrNull(
"clipboard",
"Spotify Link",
) {
name("invokeSuspend")
returnType("Ljava/lang/Object;")
parameterTypes("Ljava/lang/Object;")
strings("clipboard", "Spotify Link")
custom { method, _ ->
method.name == "invokeSuspend"
}
}
internal val BytecodePatchContext.oldShareCopyUrlMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.oldShareCopyUrlMethod by gettingFirstMutableMethodDeclaratively(
"clipboard",
"createNewSession failed",
) {
name("apply")
returnType("Ljava/lang/Object;")
parameterTypes("Ljava/lang/Object;")
strings("clipboard", "createNewSession failed")
custom { method, _ ->
method.name == "apply"
}
}
internal val BytecodePatchContext.formatAndroidShareSheetUrlMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.formatAndroidShareSheetUrlMethod by gettingFirstMutableMethodDeclarativelyOrNull {
returnType("Ljava/lang/String;")
parameterTypes("L", "Ljava/lang/String;")
opcodes(
@@ -38,16 +34,12 @@ internal val BytecodePatchContext.formatAndroidShareSheetUrlMethod by gettingFir
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT,
)
literal {
'\n'.code.toLong()
}
literal { '\n'.code.toLong() }
}
internal val BytecodePatchContext.oldFormatAndroidShareSheetUrlMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.oldFormatAndroidShareSheetUrlMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC)
returnType("Ljava/lang/String;")
parameterTypes("Lcom/spotify/share/social/sharedata/ShareData;", "Ljava/lang/String;")
literal {
'\n'.code.toLong()
}
instructions('\n'.code.toLong()())
}

View File

@@ -2,13 +2,12 @@ package app.revanced.patches.spotify.misc.privacy
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.methodReference
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch;"
@@ -25,15 +24,15 @@ val `Sanitize sharing links` by creatingBytecodePatch(
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
"sanitizeSharingLink(Ljava/lang/String;)Ljava/lang/String;"
val copyFingerprint = if (shareCopyUrlMethod.immutableMethodOrNull != null) {
val copyMethod = if (shareCopyUrlMethod != null) {
shareCopyUrlMethod
} else {
oldShareCopyUrlMethod
}
copyFingerprint.method.apply {
copyMethod!!.apply {
val newPlainTextInvokeIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "newPlainText"
methodReference?.name == "newPlainText"
}
val urlRegister = getInstruction<FiveRegisterInstruction>(newPlainTextInvokeIndex).registerD
@@ -48,9 +47,8 @@ val `Sanitize sharing links` by creatingBytecodePatch(
// Android native share sheet is used for all other quick share types (X, WhatsApp, etc).
val shareUrlParameter: String
val shareSheetFingerprint = if (formatAndroidShareSheetUrlMethod.immutableMethodOrNull != null) {
val methodAccessFlags = formatAndroidShareSheetUrlMethod.immutableMethod
shareUrlParameter = if (AccessFlags.STATIC.isSet(methodAccessFlags.accessFlags)) {
val shareSheetMethod = if (formatAndroidShareSheetUrlMethod != null) {
shareUrlParameter = if (AccessFlags.STATIC.isSet(formatAndroidShareSheetUrlMethod!!.accessFlags)) {
// In newer implementations the method is static, so p0 is not `this`.
"p1"
} else {
@@ -65,7 +63,7 @@ val `Sanitize sharing links` by creatingBytecodePatch(
oldFormatAndroidShareSheetUrlMethod
}
shareSheetFingerprint.method.addInstructions(
shareSheetMethod!!.addInstructions(
0,
"""
invoke-static { $shareUrlParameter }, $extensionMethodDescriptor

View File

@@ -1,11 +1,10 @@
package app.revanced.patches.spotify.misc.widgets
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.opcodes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val BytecodePatchContext.canBindAppWidgetPermissionMethod by gettingFirstMethodDeclaratively {
strings("android.permission.BIND_APPWIDGET")
internal val BytecodePatchContext.canBindAppWidgetPermissionMethod by gettingFirstMutableMethodDeclaratively("android.permission.BIND_APPWIDGET") {
opcodes(Opcode.AND_INT_LIT8)
}

View File

@@ -1,19 +1,25 @@
package app.revanced.patches.strava.mediaupload
internal val BytecodePatchContext.getCompressionQualityMethod by gettingFirstMethodDeclaratively {
custom { method, _ ->
method.name == "getCompressionQuality"
}
import app.revanced.patcher.definingClass
import app.revanced.patcher.firstMutableMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.iface.ClassDef
context(_: BytecodePatchContext)
internal fun ClassDef.getGetCompressionQualityMethod() = firstMutableMethodDeclaratively {
name("getCompressionQuality")
definingClass { endsWith("/MediaUploadParameters;") }
}
internal val BytecodePatchContext.getMaxDurationMethod by gettingFirstMethodDeclaratively {
custom { method, _ ->
method.name == "getMaxDuration"
}
context(_: BytecodePatchContext)
internal fun ClassDef.getGetMaxDurationMethod() = firstMutableMethodDeclaratively {
name("getMaxDuration")
definingClass { endsWith("/MediaUploadParameters;") }
}
internal val BytecodePatchContext.getMaxSizeMethod by gettingFirstMethodDeclaratively {
custom { method, _ ->
method.name == "getMaxSize"
}
context(_: BytecodePatchContext)
internal fun ClassDef.getGetMaxSizeMethod() = firstMutableMethodDeclaratively {
name("getMaxSize")
definingClass { endsWith("/MediaUploadParameters;") }
}

View File

@@ -31,15 +31,15 @@ val `Overwrite media upload parameters` by creatingBytecodePatch(
val mediaUploadParametersClass = firstClassDef { type.endsWith("/MediaUploadParameters;") }
compressionQuality?.let { compressionQuality ->
getCompressionQualityMethod.match(mediaUploadParametersClass).method.returnEarly(compressionQuality / 100f)
mediaUploadParametersClass.getGetCompressionQualityMethod().returnEarly(compressionQuality / 100f)
}
maxDuration?.let { maxDuration ->
getMaxDurationMethod.match(mediaUploadParametersClass).method.returnEarly(maxDuration)
mediaUploadParametersClass.getGetMaxDurationMethod().returnEarly(maxDuration)
}
maxSize?.let {
getMaxSizeMethod.match(mediaUploadParametersClass).method.returnEarly(it)
mediaUploadParametersClass.getGetMaxSizeMethod().returnEarly(it)
}
}
}

View File

@@ -1,7 +1,7 @@
package app.revanced.patches.tiktok.misc.login.disablerequirement
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused", "ObjectPropertyName")
val `Disable login requirement` by creatingBytecodePatch {
@@ -14,14 +14,6 @@ val `Disable login requirement` by creatingBytecodePatch {
listOf(
mandatoryLoginServiceMethod,
mandatoryLoginService2Method,
).forEach { fingerprint ->
fingerprint.method.addInstructions(
0,
"""
const/4 v0, 0x0
return v0
""",
)
}
).forEach { method -> method.returnEarly() }
}
}

View File

@@ -1,15 +1,14 @@
package app.revanced.patches.tiktok.misc.login.disablerequirement
internal val BytecodePatchContext.mandatoryLoginServiceMethod by gettingFirstMethodDeclaratively {
custom { method, classDef ->
classDef.endsWith("/MandatoryLoginService;") &&
method.name == "enableForcedLogin"
}
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
internal val BytecodePatchContext.mandatoryLoginServiceMethod by gettingFirstMutableMethodDeclaratively {
name("enableForcedLogin")
definingClass { endsWith("/MandatoryLoginService;") }
}
internal val BytecodePatchContext.mandatoryLoginService2Method by gettingFirstMethodDeclaratively {
custom { method, classDef ->
classDef.endsWith("/MandatoryLoginService;") &&
method.name == "shouldShowForcedLogin"
}
internal val BytecodePatchContext.mandatoryLoginService2Method by gettingFirstMutableMethodDeclaratively {
name("shouldShowForcedLogin")
definingClass { endsWith("/MandatoryLoginService;") }
}

View File

@@ -1,9 +1,6 @@
package app.revanced.patches.tiktok.misc.settings
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
internal val BytecodePatchContext.addSettingsEntryMethod by gettingFirstMutableMethodDeclaratively {
@@ -20,16 +17,9 @@ internal val BytecodePatchContext.settingsEntryMethod by gettingFirstMethodDecla
"pls pass item or extends the EventUnit",
)
internal val BytecodePatchContext.settingsEntryInfoMethod by gettingFirstMethodDeclaratively {
strings(
"ExposeItem(title=",
", icon=",
)
}
internal val BytecodePatchContext.settingsEntryInfoMethod by gettingFirstMethod("ExposeItem(title=", ", icon=")
internal val BytecodePatchContext.settingsStatusLoadMethod by gettingFirstMethodDeclaratively {
custom { method, classDef ->
classDef.endsWith("Lapp/revanced/extension/tiktok/settings/SettingsStatus;") &&
method.name == "load"
}
name("load")
definingClass { endsWith("Lapp/revanced/extension/tiktok/settings/SettingsStatus;") }
}

View File

@@ -1,15 +1,12 @@
package app.revanced.patches.tiktok.shared
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 com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val BytecodePatchContext.getEnterFromMethod by gettingFirstMethodDeclaratively {
definingClass { endsWith("/BaseListFragmentPanel;") }
returnType("Ljava/lang/String;")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("Z")
@@ -22,14 +19,8 @@ internal val BytecodePatchContext.getEnterFromMethod by gettingFirstMethodDeclar
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT,
)
custom { methodDef, _ ->
methodDef.definingClass.endsWith("/BaseListFragmentPanel;")
}
}
internal val BytecodePatchContext.onRenderFirstFrameMethod by gettingFirstMethodDeclaratively {
strings("method_enable_viewpager_preload_duration")
custom { _, classDef ->
classDef.endsWith("/BaseListFragmentPanel;")
}
internal val BytecodePatchContext.onRenderFirstFrameMethod by gettingFirstMutableMethodDeclaratively("method_enable_viewpager_preload_duration") {
definingClass { endsWith("/BaseListFragmentPanel;") }
}

View File

@@ -1,28 +1,16 @@
package app.revanced.patches.trakt
import app.revanced.patcher.custom
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal val BytecodePatchContext.isVIPEPMethod by gettingFirstMutableMethodDeclaratively {
custom { method, classDef ->
if (!classDef.endsWith("RemoteUser;")) return@custom false
method.name == "isVIPEP"
}
name("isVIPEP")
definingClass { endsWith("RemoteUser;") }
}
internal val BytecodePatchContext.isVIPMethod by gettingFirstMutableMethodDeclaratively {
custom { method, classDef ->
if (!classDef.endsWith("RemoteUser;")) return@custom false
method.name == "isVIP"
}
name("isVIP")
definingClass { endsWith("RemoteUser;") }
}
internal val BytecodePatchContext.remoteUserMethod by gettingFirstMutableMethodDeclaratively {
custom { _, classDef ->
classDef.endsWith("RemoteUser;")
}
}
// TODO

View File

@@ -1,20 +1,17 @@
package app.revanced.patches.trakt
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.creatingBytecodePatch
import com.android.tools.smali.dexlib2.mutable.MutableMethod
@Suppress("unused", "ObjectPropertyName")
val `Unlock pro` by creatingBytecodePatch {
compatibleWith("tv.trakt.trakt"("1.1.1"))
apply {
arrayOf(isVIPMethod, isVIPEPMethod).onEach { fingerprint ->
// Resolve both fingerprints on the same class.
fingerprint.match(remoteUserMethod.immutableClassDef) // TODO
}.forEach { fingerprint ->
// Return true for both VIP check methods.
fingerprint.addInstructions(
// Return true for both VIP check methods.
arrayOf(isVIPMethod, isVIPEPMethod).forEach { method: MutableMethod ->
method.addInstructions(
0,
"""
const/4 v0, 0x1

View File

@@ -1,19 +1,14 @@
package app.revanced.patches.tudortmund.lockscreen
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
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
internal val BytecodePatchContext.brightnessMethod by gettingFirstMethodDeclaratively {
name("run")
definingClass { contains("/ScreenPlugin$") }
accessFlags(AccessFlags.PUBLIC)
returnType("V")
parameterTypes()
custom { method, classDef ->
method.name == "run" &&
method.definingClass.contains("/ScreenPlugin\$") &&
classDef.fields.any { it.type == "Ljava/lang/Float;" }
}
custom { immutableClassDef.anyField { type == "Ljava/lang/Float;" } }
}

View File

@@ -1,9 +1,13 @@
package app.revanced.patches.tumblr.annoyances.notifications
import app.revanced.patcher.gettingFirstMutableMethod
import app.revanced.patcher.patch.BytecodePatchContext
// The BlogNotifyCtaDialog asks you if you want to enable notifications for a blog.
// It shows whenever you visit a certain blog for the second time and disables itself
// if it was shown a total of 3 times (stored in app storage).
// This targets the BlogNotifyCtaDialog.isEnabled() method to let it always return false.
internal val BytecodePatchContext.isBlogNotifyEnabledMethod by gettingFirstMethodDeclaratively {
strings("isEnabled --> ", "blog_notify_enabled")
}
internal val BytecodePatchContext.isBlogNotifyEnabledMethod by gettingFirstMutableMethod(
"isEnabled --> ",
"blog_notify_enabled",
)

View File

@@ -3,7 +3,7 @@ package app.revanced.patches.twitch.ad.shared.util
import app.revanced.patcher.extensions.ExternalLabel
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.firstClassDefMutableOrNull
import app.revanced.patcher.firstMutableClassDefOrNull
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
@@ -30,10 +30,10 @@ fun adPatch(
classDefType: String,
methodNames: Set<String>,
returnMethod: ReturnMethod,
) = with(firstClassDefMutableOrNull(classDefType)) {
this ?: return false
): Boolean {
val classDef = firstMutableClassDefOrNull(classDefType) ?: return false
methods.filter { it.name in methodNames }.forEach {
classDef.methods.filter { it.name in methodNames }.forEach { method ->
val retInstruction = when (returnMethod.returnType) {
'V' -> "return-void"
'Z' ->
@@ -45,17 +45,17 @@ fun adPatch(
else -> throw NotImplementedError()
}
it.addInstructionsWithLabels(
method.addInstructionsWithLabels(
0,
"""
${createConditionInstructions("v0")}
$retInstruction
""",
ExternalLabel(skipLabelName, it.getInstruction(0)),
ExternalLabel(skipLabelName, method.getInstruction(0)),
)
}
true
return true
}
block(::createConditionInstructions, BytecodePatchContext::blockMethods)

View File

@@ -145,7 +145,7 @@ val Settings by creatingBytecodePatch(
)
// Intercept onclick events for the settings menu
menuGroupsOnClickFingerprint.method.addInstructionsWithLabels(
menuGroupsOnClickMethod.addInstructionsWithLabels(
0,
"""
invoke-static {p1}, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuOnClick(Ljava/lang/Enum;)Z
@@ -157,7 +157,7 @@ val Settings by creatingBytecodePatch(
""",
ExternalLabel(
"no_rv_settings_onclick",
menuGroupsOnClickFingerprint.method.getInstruction(0),
menuGroupsOnClickMethod.getInstruction(0),
),
)
}

View File

@@ -1,36 +1,29 @@
package app.revanced.patches.twitter.misc.links
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
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
internal val BytecodePatchContext.sanitizeSharingLinksMethod by gettingFirstMethodDeclaratively {
returnType("Ljava/lang/String;")
strings("<this>", "shareParam", "sessionToken")
}
internal val BytecodePatchContext.sanitizeSharingLinksMethod by gettingFirstMutableMethod(
"<this>",
"shareParam",
"sessionToken",
) { returnType == "Ljava/lang/String;" }
// Returns a shareable link string based on a tweet ID and a username.
internal val BytecodePatchContext.linkBuilderMethod by gettingFirstMethodDeclaratively {
strings("/%1\$s/status/%2\$d")
}
internal val BytecodePatchContext.linkBuilderMethod by gettingFirstMutableMethod($$"/%1$s/status/%2$d")
// TODO remove this once changeLinkSharingDomainResourcePatch is restored
// Returns a shareable link for the "Share via..." dialog.
internal val BytecodePatchContext.linkResourceGetterMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.linkResourceGetterMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("Landroid/content/res/Resources;")
custom { _, classDef ->
classDef.fields.any { field ->
field.type.startsWith("Lcom/twitter/model/core/")
}
custom {
immutableClassDef.anyField { type.startsWith("Lcom/twitter/model/core/") }
}
}
internal val BytecodePatchContext.linkSharingDomainHelperMethod by gettingFirstMethodDeclaratively {
custom { method, classDef ->
method.name == "getShareDomain" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
internal val BytecodePatchContext.linkSharingDomainHelperMethod by gettingFirstMutableMethodDeclaratively {
name("getShareDomain")
definingClass(EXTENSION_CLASS_DESCRIPTOR)
}

View File

@@ -1,18 +1,16 @@
package app.revanced.patches.viber.misc.navbar
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.firstMutableMethodDeclaratively
import app.revanced.patcher.gettingFirstMethod
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.iface.ClassDef
internal val BytecodePatchContext.tabIdClassMethod by gettingFirstMethodDeclaratively {
strings("shouldShowTabId")
}
internal val BytecodePatchContext.tabIdClassMethod by gettingFirstMethod("shouldShowTabId")
context(BytecodePatchContext)
internal val shouldShowTabIdMethodFingerprint get() = fingerprint {
context(_: BytecodePatchContext)
internal fun ClassDef.getShouldShowTabIdMethod() = firstMutableMethodDeclaratively {
parameterTypes("I", "I")
returnType("Z")
custom { methodDef, classDef ->
classDef == tabIdClassMethod.classDef
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.viber.misc.navbar
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.creatingBytecodePatch
import java.util.logging.Logger
@@ -8,7 +9,7 @@ import java.util.logging.Logger
@Suppress("unused", "ObjectPropertyName")
val `Hide navigation buttons` by creatingBytecodePatch(
description = "Permanently hides navigation bar buttons, such as Explore and Marketplace.",
use = false
use = false,
) {
compatibleWith("com.viber.voip")
@@ -26,7 +27,7 @@ val `Hide navigation buttons` by creatingBytecodePatch(
if (allowedItems.size == AllowedNavigationItems.entries.size) {
return@apply Logger.getLogger(this::class.java.name).warning(
"No hide navigation buttons options are enabled. No changes applied."
"No hide navigation buttons options are enabled. No changes applied.",
)
}
@@ -42,7 +43,7 @@ val `Hide navigation buttons` by creatingBytecodePatch(
nop
"""
shouldShowTabIdMethodFingerprint.method
tabIdClassMethod.immutableClassDef.getShouldShowTabIdMethod()
.addInstructionsWithLabels(0, injectionInstructions)
}
}
@@ -54,7 +55,7 @@ val `Hide navigation buttons` by creatingBytecodePatch(
private enum class AllowedNavigationItems(
val defaultHideOption: Boolean,
private val itemName: String,
private vararg val ids: Int
private vararg val ids: Int,
) {
CHATS(false, "Chats", 0),
CALLS(false, "Calls", 1, 7),
@@ -62,16 +63,16 @@ private enum class AllowedNavigationItems(
MORE(false, "More", 3),
PAY(true, "Pay", 5),
CAMERA(true, "Camera", 6),
MARKETPLACE(true, "Marketplace", 8);
MARKETPLACE(true, "Marketplace", 8),
;
val optionName = "Hide $itemName"
val description = "Permanently hides the $itemName button."
fun buildAllowInstruction(): String =
ids.joinToString("\n") { id ->
"""
fun buildAllowInstruction(): String = ids.joinToString("\n") { id ->
"""
const/4 v0, $id # If tabId == $id ($itemName), don't hide it
if-eq p1, v0, :continue
"""
}
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.ad.general
import app.revanced.patcher.accessFlags
import app.revanced.patcher.custom
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
@@ -16,9 +17,9 @@ internal val BytecodePatchContext.fullScreenEngagementAdContainerMethod by getti
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes()
custom { method, _ ->
method.containsLiteralInstruction(fullScreenEngagementAdContainer) &&
indexOfAddListInstruction(method) >= 0
custom {
containsLiteralInstruction(fullScreenEngagementAdContainer) &&
indexOfAddListInstruction(this) >= 0
}
}

View File

@@ -1,8 +1,9 @@
package app.revanced.patches.youtube.ad.video
internal val BytecodePatchContext.loadVideoAdsMethod by gettingFirstMethodDeclaratively {
strings(
"TriggerBundle doesn't have the required metadata specified by the trigger ",
"Ping migration no associated ping bindings for activated trigger: ",
)
}
import app.revanced.patcher.gettingFirstMethod
import app.revanced.patcher.patch.BytecodePatchContext
internal val BytecodePatchContext.loadVideoAdsMethod by gettingFirstMethod(
"TriggerBundle doesn't have the required metadata specified by the trigger ",
"Ping migration no associated ping bindings for activated trigger: ",
)

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.youtube.interaction.doubletap
import app.revanced.patcher.*
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patches.all.misc.resources.addResources
@@ -13,7 +14,6 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import java.util.logging.Logger
import kotlin.jvm.java
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableDoubleTapActionsPatch;"
@@ -53,7 +53,7 @@ val `Disable double tap actions` by creatingBytecodePatch(
SwitchPreference("revanced_disable_chapter_skip_double_tap"),
)
val doubleTapInfoGetSeekSourceFingerprint = fingerprint {
val doubleTapInfoGetSeekSourceMethod = firstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("Z")
returnType(seekTypeEnumMethod.immutableClassDef.type)
@@ -64,13 +64,11 @@ val `Disable double tap actions` by creatingBytecodePatch(
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
)
custom { _, classDef ->
classDef.fields.count() == 4
}
custom { immutableClassDef.fields.count() == 4 }
}
// Force isChapterSeek flag to false.
doubleTapInfoGetSeekSourceFingerprint.method.addInstructions(
doubleTapInfoGetSeekSourceMethod.addInstructions(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->disableDoubleTapChapters(Z)Z
@@ -78,9 +76,7 @@ val `Disable double tap actions` by creatingBytecodePatch(
""",
)
doubleTapInfoCtorMethod.match(
doubleTapInfoGetSeekSourceFingerprint.classDef,
).method.addInstructions(
doubleTapInfoGetSeekSourceMethod.immutableClassDef.getDoubleTapInfoCtorMethod().addInstructions(
0,
"""
invoke-static { p3 }, $EXTENSION_CLASS_DESCRIPTOR->disableDoubleTapChapters(Z)Z

View File

@@ -1,25 +1,27 @@
package app.revanced.patches.youtube.interaction.doubletap
import app.revanced.patcher.accessFlags
import app.revanced.patcher.firstMutableMethodDeclaratively
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.ClassDef
internal val BytecodePatchContext.seekTypeEnumMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.seekTypeEnumMethod by gettingFirstMethodDeclaratively(
"SEEK_SOURCE_SEEK_TO_NEXT_CHAPTER",
"SEEK_SOURCE_SEEK_TO_PREVIOUS_CHAPTER",
) {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
strings(
"SEEK_SOURCE_SEEK_TO_NEXT_CHAPTER",
"SEEK_SOURCE_SEEK_TO_PREVIOUS_CHAPTER",
)
}
internal val BytecodePatchContext.doubleTapInfoCtorMethod by gettingFirstMethodDeclaratively {
context(_: BytecodePatchContext)
internal fun ClassDef.getDoubleTapInfoCtorMethod() = firstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameterTypes(
"Landroid/view/MotionEvent;",
"I",
"Z",
"Lj\$/time/Duration;",
"Lj$/time/Duration;",
)
}

View File

@@ -1,11 +1,7 @@
package app.revanced.patches.youtube.interaction.downloads
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
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
internal val BytecodePatchContext.offlineVideoEndpointMethod by gettingFirstMethodDeclaratively {

View File

@@ -1,32 +1,27 @@
package app.revanced.patches.youtube.layout.autocaptions
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 com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val BytecodePatchContext.startVideoInformerMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.startVideoInformerMethod by gettingFirstMethodDeclaratively("pc") {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
opcodes(
Opcode.INVOKE_INTERFACE,
Opcode.RETURN_VOID,
)
strings("pc")
}
internal val BytecodePatchContext.storyboardRendererDecoderRecommendedLevelMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.storyboardRendererDecoderRecommendedLevelMethod by gettingFirstMethodDeclaratively("#-1#") {
returnType("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("L")
strings("#-1#")
}
internal val BytecodePatchContext.subtitleTrackMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.subtitleTrackMethod by gettingFirstMethodDeclaratively("DISABLE_CAPTIONS_OPTION") {
definingClass { endsWith("/SubtitleTrack;") }
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Z")
parameterTypes()
@@ -38,8 +33,4 @@ internal val BytecodePatchContext.subtitleTrackMethod by gettingFirstMethodDecla
Opcode.MOVE_RESULT,
Opcode.RETURN,
)
strings("DISABLE_CAPTIONS_OPTION")
custom { _, classDef ->
classDef.endsWith("/SubtitleTrack;")
}
}

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.youtube.layout.formfactor
import app.revanced.patcher.*
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.instructions
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -46,21 +46,19 @@ val `Change form factor` by creatingBytecodePatch(
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
val createPlayerRequestBodyWithModelFingerprint = fingerprint {
val formFactorEnumConstructorClass = formFactorEnumConstructorMethod.definingClass
val createPlayerRequestBodyWithModelMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("L")
parameterTypes()
instructions(
fieldAccess(smali = "Landroid/os/Build;->MODEL:Ljava/lang/String;"),
fieldAccess(
definingClass = formFactorEnumConstructorMethod.immutableClassDef.type,
type = "I",
afterAtMost(50),
),
field { name == "MODEL" && definingClass == "Landroid/os/Build;" },
field { type == "I" && definingClass == formFactorEnumConstructorClass },
)
}
createPlayerRequestBodyWithModelFingerprint.let {
createPlayerRequestBodyWithModelMatch.let {
it.method.apply {
val index = it.indices.last()
val register = getInstruction<TwoRegisterInstruction>(index).registerA

View File

@@ -5,12 +5,11 @@ import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
internal val BytecodePatchContext.formFactorEnumConstructorMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.formFactorEnumConstructorMethod by gettingFirstMethodDeclaratively(
"UNKNOWN_FORM_FACTOR",
"SMALL_FORM_FACTOR",
"LARGE_FORM_FACTOR",
"AUTOMOTIVE_FORM_FACTOR",
) {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
strings(
"UNKNOWN_FORM_FACTOR",
"SMALL_FORM_FACTOR",
"LARGE_FORM_FACTOR",
"AUTOMOTIVE_FORM_FACTOR",
)
}

View File

@@ -1,18 +1,13 @@
package app.revanced.patches.youtube.layout.hide.fullscreenambientmode
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
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
internal val BytecodePatchContext.setFullScreenBackgroundColorMethod by gettingFirstMethodDeclaratively {
name("onLayout")
definingClass { endsWith("/YouTubePlayerViewNotForReflection;") }
returnType("V")
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
parameterTypes("Z", "I", "I", "I", "I")
custom { method, classDef ->
classDef.type.endsWith("/YouTubePlayerViewNotForReflection;") &&
method.name == "onLayout"
}
}

View File

@@ -2,10 +2,10 @@ package app.revanced.patches.youtube.layout.hide.shorts
import app.revanced.patcher.accessFlags
import app.revanced.patcher.after
import app.revanced.patcher.at
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.method
import app.revanced.patcher.opcodes
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
@@ -21,7 +21,7 @@ internal val BytecodePatchContext.shortsBottomBarContainerMethod by gettingFirst
instructions(
"r_pfvc"(),
ResourceType.ID("bottom_bar_container"),
methodCall(name = "getHeight"),
method("getHeight"),
Opcode.MOVE_RESULT(),
)
}

View File

@@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -95,7 +96,8 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
SwitchPreference("revanced_hide_shorts_effect_button"),
SwitchPreference("revanced_hide_shorts_green_screen_button"),
SwitchPreference("revanced_hide_shorts_hashtag_button"),
SwitchPreference("revanced_hide_shorts_live_preview"), SwitchPreference("revanced_hide_shorts_new_posts_button"),
SwitchPreference("revanced_hide_shorts_live_preview"),
SwitchPreference("revanced_hide_shorts_new_posts_button"),
SwitchPreference("revanced_hide_shorts_shop_button"),
SwitchPreference("revanced_hide_shorts_tagged_products"),
SwitchPreference("revanced_hide_shorts_search_suggestions"),
@@ -103,7 +105,8 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
SwitchPreference("revanced_hide_shorts_stickers"),
// Bottom of the screen.
SwitchPreference("revanced_hide_shorts_auto_dubbed_label"), SwitchPreference("revanced_hide_shorts_location_label"),
SwitchPreference("revanced_hide_shorts_auto_dubbed_label"),
SwitchPreference("revanced_hide_shorts_location_label"),
SwitchPreference("revanced_hide_shorts_channel_bar"),
SwitchPreference("revanced_hide_shorts_info_panel"),
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
@@ -154,7 +157,7 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/ShortsFilter;"
@Suppress("unused", "ObjectPropertyName")
val `Hide Shorts componentsby creatingBytecodePatch(
val `Hide Shorts components` by creatingBytecodePatch(
description = "Adds options to hide components related to Shorts.",
) {
dependsOn(