a couple more fixes

This commit is contained in:
oSumAtrIX 2026-02-02 15:59:38 +01:00
parent a8572bd4c7
commit 4a1d850bd4
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
34 changed files with 184 additions and 155 deletions

View file

@ -7,13 +7,16 @@ import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
@Deprecated(
"Use forEachInstructionAsSequence directly within a bytecodePatch", ReplaceWith(
"bytecodePatch { apply { forEachInstructionAsSequence(filterMap, transform) } }",
"app.revanced.util.forEachInstructionAsSequence",
"app.revanced.patcher.patch.bytecodePatch",
)
)
fun <T> transformInstructionsPatch(
filterMap: (ClassDef, Method, Instruction, Int) -> T?,
transform: (MutableMethod, T) -> Unit,
) = bytecodePatch {
apply {
forEachInstructionAsSequence { classDef, method, i, instruction ->
transform(method, filterMap(classDef, method, instruction, i) ?: return@forEachInstructionAsSequence)
}
}
apply { forEachInstructionAsSequence(filterMap, transform) }
}

View file

@ -6,7 +6,7 @@ import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val hideSuggestedContent = bytecodePatch(
val hideSuggestedContentPatch = bytecodePatch(
name = "Hide suggested content",
description = "Hides suggested stories, reels, threads and survey from feed (Suggested posts will still be shown).",
use = false,

View file

@ -1,9 +1,6 @@
package app.revanced.patches.music.misc.androidauto
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.util.returnEarly
@Deprecated("This patch is useless by itself and has been merged into another patch.", ReplaceWith("unlockAndroidAutoMediaBrowserPatch"))
@Suppress("unused")

View file

@ -5,7 +5,6 @@ import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val signatureDetectionPatch = bytecodePatch(
description = "Disables detection of incorrect signature.",
) {

View file

@ -3,7 +3,6 @@ package app.revanced.patches.photomath.misc.unlock.bookpoint
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val enableBookpointPatch = bytecodePatch(
description = "Enables textbook access",
) {

View file

@ -3,7 +3,6 @@ package app.revanced.patches.reddit.ad.comments
import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val hideCommentAdsPatch = bytecodePatch(
description = "Removes ads in the comments."
) {

View file

@ -60,34 +60,41 @@ internal fun sanitizeSharingLinksPatch(
},
)
fun Match.hook(
getInsertIndex: List<Int>.() -> Int,
getUrlRegister: MutableMethod.(insertIndex: Int) -> Int,
) {
val insertIndex = indices[0].getInsertIndex()
val urlRegister = method.getUrlRegister(insertIndex)
fun Match.hookUrlString(matchIndex: Int) {
val index = get(matchIndex)
val urlRegister = method.getInstruction<OneRegisterInstruction>(index).registerA
method.addInstructions(
insertIndex,
index + 1,
"""
invoke-static {v$urlRegister}, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String;
invoke-static { v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$urlRegister
""",
"""
)
}
// YouTube share sheet.\
youTubeShareSheetMethodMatch.hook(getInsertIndex = { first() + 1 }) { insertIndex ->
getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
fun Match.hookIntentPutExtra(matchIndex: Int) {
val index = get(matchIndex)
val urlRegister = method.getInstruction<FiveRegisterInstruction>(index).registerE
method.addInstructionsAtControlFlowLabel(
index,
"""
invoke-static { v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$urlRegister
"""
)
}
// YouTube share sheet copy link.
youTubeCopyTextMethodMatch.hookUrlString(0)
// YouTube share sheet other apps.
youTubeShareSheetMethodMatch.hookIntentPutExtra(3)
// Native system share sheet.
youTubeSystemShareSheetMethodMatch.hook(getInsertIndex = { last() }) { insertIndex ->
getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
}
youTubeCopyTextMethodMatch.hook(getInsertIndex = { first() + 2 }) { insertIndex ->
getInstruction<TwoRegisterInstruction>(insertIndex - 2).registerA
}
youTubeSystemShareSheetMethodMatch.hookIntentPutExtra(3)
}
}

View file

@ -93,7 +93,7 @@ private val addGiveKudosButtonToLayoutPatch = resourcePatch {
}
@Suppress("unused")
val addGiveGroupKudosButtonToGroupActivity = bytecodePatch(
val addGiveGroupKudosButtonToGroupActivityPatch = bytecodePatch(
name = "Add 'Give Kudos' button to 'Group Activity'",
description = "Adds a button that triggers the same action as shaking your phone would.",
) {

View file

@ -4,7 +4,7 @@ import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.Settings
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -19,7 +19,7 @@ val feedFilterPatch = bytecodePatch(
) {
dependsOn(
sharedExtensionPatch,
Settings,
settingsPatch,
)
compatibleWith(

View file

@ -3,7 +3,7 @@ package app.revanced.patches.tiktok.interaction.downloads
import app.revanced.patcher.extensions.*
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.Settings
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
@ -15,12 +15,13 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/download/DownloadsPatch;"
@Suppress("unused")
val Downloads = bytecodePatch(
val downloadsPatch = bytecodePatch(
name = "Downloads",
description = "Removes download restrictions and changes the default path to download to.",
) {
dependsOn(
sharedExtensionPatch,
Settings,
settingsPatch,
)
compatibleWith(

View file

@ -12,7 +12,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tiktok/settings/TikTokActivityHook;"
val Settings = bytecodePatch(
val settingsPatch = bytecodePatch(
name = "Settings",
description = "Adds ReVanced settings to TikTok.",
) {
dependsOn(sharedExtensionPatch, addBrandLicensePatch)

View file

@ -6,7 +6,7 @@ import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.Settings
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -21,7 +21,7 @@ val sIMSpoofPatch = bytecodePatch(
) {
dependsOn(
sharedExtensionPatch,
Settings,
settingsPatch,
)
compatibleWith(

View file

@ -9,7 +9,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused")
val blockAudioAdsPatch = bytecodePatch(
@ -18,7 +18,7 @@ val blockAudioAdsPatch = bytecodePatch(
) {
dependsOn(
sharedExtensionPatch,
Settings,
settingsPatch,
addResourcesPatch,
)

View file

@ -7,7 +7,7 @@ import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.twitch.ad.video.blockVideoAdsPatch
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused")
val blockEmbeddedAdsPatch = bytecodePatch(
@ -17,7 +17,7 @@ val blockEmbeddedAdsPatch = bytecodePatch(
dependsOn(
blockVideoAdsPatch,
sharedExtensionPatch,
Settings,
settingsPatch,
)
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))

View file

@ -12,7 +12,7 @@ import app.revanced.patches.twitch.ad.shared.util.ReturnMethod
import app.revanced.patches.twitch.ad.shared.util.adPatch
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("ObjectPropertyName")
val blockVideoAdsPatch = bytecodePatch(
@ -24,7 +24,7 @@ val blockVideoAdsPatch = bytecodePatch(
dependsOn(
sharedExtensionPatch,
Settings,
settingsPatch,
addResourcesPatch,
adPatch(conditionCall, skipLabelName) { createConditionInstructions, blockMethods ->

View file

@ -10,7 +10,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused")
val showDeletedMessagesPatch = bytecodePatch(
@ -19,7 +19,7 @@ val showDeletedMessagesPatch = bytecodePatch(
) {
dependsOn(
sharedExtensionPatch,
Settings,
settingsPatch,
addResourcesPatch,
)

View file

@ -9,7 +9,7 @@ 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.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused")
val autoClaimChannelPointsPatch = bytecodePatch(
@ -17,7 +17,7 @@ val autoClaimChannelPointsPatch = bytecodePatch(
description = "Automatically claim Channel Points.",
) {
dependsOn(
Settings,
settingsPatch,
addResourcesPatch,
)

View file

@ -7,7 +7,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("ObjectPropertyName")
val debugModePatch = bytecodePatch(
@ -17,7 +17,7 @@ val debugModePatch = bytecodePatch(
) {
dependsOn(
sharedExtensionPatch,
Settings,
settingsPatch,
addResourcesPatch,
)

View file

@ -36,7 +36,8 @@ fun addSettingPreference(screen: BasePreference) {
preferences += screen
}
val Settings = bytecodePatch(
val settingsPatch = bytecodePatch(
name = "Settings",
description = "Adds settings menu to Twitch.",
) {
dependsOn(

View file

@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
@ -94,21 +95,25 @@ val hideAdsPatch = bytecodePatch(
replaceInstruction(
addListIndex,
"invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" +
"->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V",
"->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V",
)
}
// Hide ad views.
forEachInstructionAsSequence { _, method, index, instruction ->
if (instruction.opcode != Opcode.CONST) return@forEachInstructionAsSequence
if (instruction.wideLiteral != adAttributionId) return@forEachInstructionAsSequence
forEachInstructionAsSequence({ _, method, instruction, index ->
if (instruction.opcode != Opcode.CONST) return@forEachInstructionAsSequence null
if (instruction.wideLiteral != adAttributionId) return@forEachInstructionAsSequence null
val insertIndex = index + 1
// Call to get the view with the id adAttribution,
if (method.instructions[insertIndex].opcode != Opcode.INVOKE_VIRTUAL) return@forEachInstructionAsSequence
// Call to get the view with the id adAttribution.
if (method.instructions.elementAt(insertIndex).opcode != Opcode.INVOKE_VIRTUAL) return@forEachInstructionAsSequence null
val viewRegister = method.getInstruction<FiveRegisterInstruction>(insertIndex).registerC
return@forEachInstructionAsSequence insertIndex to viewRegister
}) { method, (insertIndex, viewRegister) ->
method.injectHideViewCall(insertIndex, viewRegister, EXTENSION_CLASS_DESCRIPTOR, "hideAdAttributionView")
}
}

View file

@ -60,7 +60,8 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/
internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;"
@Suppress("unused")
val Downloads = bytecodePatch(
val downloadsPatch = bytecodePatch(
name = "Downloads",
description = "Adds support to download videos with an external downloader app " +
"using the in-app download button or a video player action button.",
) {

View file

@ -112,9 +112,9 @@ internal val BytecodePatchContext.seekbarTappingMethodMatch by composingFirstMet
Int.MAX_VALUE.toLong()(),
allOf(Opcode.NEW_INSTANCE(), type("Landroid/graphics/Point;")),
after(method { toString() == "Landroid/graphics/Point;-><init>(II)V" }),
after(method { toString() == "Landroid/view/MotionEvent;->getX()F" }),
after(method { toString() == "Lj$/util/Optional;->of(Ljava/lang/Object;)Lj$/util/Optional;" }),
after(Opcode.MOVE_RESULT_OBJECT()),
after(allOf(Opcode.IPUT_OBJECT(), field { type == "Lj\$/util/Optional;" })),
after(allOf(Opcode.IPUT_OBJECT(), field { type == "Lj$/util/Optional;" })),
afterAtMost(10, Opcode.INVOKE_VIRTUAL()),
)
}

View file

@ -13,7 +13,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.seekbarMethod
import app.revanced.patches.youtube.shared.seekbarOnDrawMethodMatch
import app.revanced.patches.youtube.shared.getSeekbarOnDrawMethodMatch
import app.revanced.util.insertLiteralOverride
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;"
@ -38,7 +38,7 @@ val hideSeekbarPatch = bytecodePatch(
SwitchPreference("revanced_fullscreen_large_seekbar"),
)
seekbarMethod.immutableClassDef.seekbarOnDrawMethodMatch.method.addInstructionsWithLabels(
seekbarMethod.immutableClassDef.getSeekbarOnDrawMethodMatch().method.addInstructionsWithLabels(
0,
"""
const/4 v0, 0x0

View file

@ -3,7 +3,8 @@ package app.revanced.patches.youtube.interaction.seekbar
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val Seekbar = bytecodePatch(
val seekbarPatch = bytecodePatch(
name = "Seekbar",
description = "Adds options to disable precise seeking when swiping up on the seekbar, " +
"slide to seek instead of playing at 2x speed when pressing and holding, " +
"tapping the player seekbar to seek, " +

View file

@ -3,6 +3,7 @@ package app.revanced.patches.youtube.layout.branding.header
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
@ -77,12 +78,15 @@ private val changeHeaderBytecodePatch = bytecodePatch {
).forEach { resourceName ->
val id = ResourceType.ATTR[resourceName]
forEachInstructionAsSequence { _, method, i, instruction ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence
forEachInstructionAsSequence({ _, method, instruction, index ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null
val register = method.getInstruction<OneRegisterInstruction>(i).registerA
val register = method.getInstruction<OneRegisterInstruction>(index).registerA
return@forEachInstructionAsSequence index to register
}) { method, (index, register) ->
method.addInstructions(
i + 1,
index + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getHeaderAttributeId(I)I
move-result v$register
@ -218,14 +222,14 @@ val changeHeaderPatch = resourcePatch(
if (!customFile.exists()) {
throw PatchException(
"The custom header path cannot be found: " +
customFile.absolutePath,
customFile.absolutePath,
)
}
if (!customFile.isDirectory) {
throw PatchException(
"The custom header path must be a folder: " +
customFile.absolutePath,
customFile.absolutePath,
)
}
@ -248,7 +252,7 @@ val changeHeaderPatch = resourcePatch(
if (customFiles.isNotEmpty() && customFiles.size != variants.size) {
throw PatchException(
"Both light/dark mode images " +
"must be specified but only found: " + customFiles.map { it.name },
"must be specified but only found: " + customFiles.map { it.name },
)
}
@ -263,8 +267,8 @@ val changeHeaderPatch = resourcePatch(
if (!copiedFiles) {
throw PatchException(
"Expected to find directories and files: " +
customHeaderResourceFileNames.contentToString() +
"\nBut none were found in the provided option file path: " + customFile.absolutePath,
customHeaderResourceFileNames.contentToString() +
"\nBut none were found in the provided option file path: " + customFile.absolutePath,
)
}
}

View file

@ -3,7 +3,9 @@ package app.revanced.patches.youtube.layout.hide.shorts
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.methodReference
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
@ -66,9 +68,9 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
// FIXME: The buffer is very different for 20.22+ and these current cannot be hidden.
Logger.getLogger(this::class.java.name).warning(
"\n!!!" +
"\n!!! Shorts action buttons currently cannot be set hidden when patching 20.22+" +
"\n!!! Patch 20.21.37 or lower if you want to hide Shorts action buttons" +
"\n!!!",
"\n!!! Shorts action buttons currently cannot be set hidden when patching 20.22+" +
"\n!!! Patch 20.21.37 or lower if you want to hide Shorts action buttons" +
"\n!!!",
)
} else {
preferences.addAll(
@ -188,16 +190,18 @@ val hideShortsComponentsPatch = bytecodePatch(
val id = ResourceType.DIMEN["reel_player_right_pivot_v2_size"]
forEachInstructionAsSequence { _, method, i, instruction ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence
forEachInstructionAsSequence({ _, method, instruction, index ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null
val targetIndex = method.indexOfFirstInstructionOrThrow(i) {
getReference<MethodReference>()?.name == "getDimensionPixelSize"
val targetIndex = method.indexOfFirstInstructionOrThrow(index) {
methodReference?.name == "getDimensionPixelSize"
} + 1
val sizeRegister = method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
method.addInstructions(
return@forEachInstructionAsSequence targetIndex to sizeRegister
}) { method, (targetIndex, sizeRegister) ->
firstMutableMethod(method).addInstructions(
targetIndex + 1,
"""
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
@ -218,24 +222,24 @@ val hideShortsComponentsPatch = bytecodePatch(
addInstruction(
insertIndex,
"invoke-static {v$viewRegister}," +
" $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V",
" $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V",
)
}
}
// Hook to hide the shared navigation bar when the Shorts player is opened.
(
if (is_20_45_or_greater) {
renderBottomNavigationBarParentMethod
} else if (is_19_41_or_greater) {
renderBottomNavigationBarLegacy1941ParentMethod
} else {
legacyRenderBottomNavigationBarLegacyParentMethod
}
).immutableClassDef.getRenderBottomNavigationBarMethodMatch().addInstruction(
0,
"invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V",
)
if (is_20_45_or_greater) {
renderBottomNavigationBarParentMethod
} else if (is_19_41_or_greater) {
renderBottomNavigationBarLegacy1941ParentMethod
} else {
legacyRenderBottomNavigationBarLegacyParentMethod
}
).immutableClassDef.getRenderBottomNavigationBarMethodMatch().addInstruction(
0,
"invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V",
)
// Hide the bottom bar container of the Shorts player.
shortsBottomBarContainerMethodMatch.let {

View file

@ -62,7 +62,8 @@ private val miniplayerResourcePatch = resourcePatch {
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/MiniplayerPatch;"
@Suppress("unused")
val Miniplayer = bytecodePatch(
val miniplayerPatch = bytecodePatch(
name = "Miniplayer",
description = "Adds options to change the in-app minimized player.",
) {
dependsOn(

View file

@ -164,27 +164,22 @@ val returnYouTubeDislikePatch = bytecodePatch(
charSequenceRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerA
}
val free1 = findFreeRegister(insertIndex, charSequenceRegister)
val free2 = findFreeRegister(insertIndex, charSequenceRegister, free1)
val conversionContext = findFreeRegister(insertIndex, charSequenceRegister)
addInstructionsAtControlFlowLabel(
insertIndex,
"""
# Copy conversion context.
move-object/from16 v$free1, p0
move-object/from16 v$conversionContext, p0
# 20.41 field is the abstract superclass.
# Verify it's the expected subclass just in case.
instance-of v$free2, v$free1, ${textComponentConversionContextField.type}
if-eqz v$free2, :ignore
iget-object v$conversionContext, v$conversionContext, $textComponentConversionContextField
check-cast v$free1, $conversionContextClass
invoke-static { v$free1, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
invoke-static { v$conversionContext, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister
:ignore
nop
""",
"""
)
}

View file

@ -22,7 +22,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.getLayoutConstructorMethodMatch
import app.revanced.patches.youtube.shared.seekbarMethod
import app.revanced.patches.youtube.shared.seekbarOnDrawMethodMatch
import app.revanced.patches.youtube.shared.getSeekbarOnDrawMethodMatch
import app.revanced.patches.youtube.video.information.onCreateHook
import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.patches.youtube.video.information.videoTimeHook
@ -111,7 +111,8 @@ private const val EXTENSION_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController;"
@Suppress("unused")
val SponsorBlock = bytecodePatch(
val sponsorBlockPatch = bytecodePatch(
name = "SponsorBlock",
description = "Adds options to enable and configure SponsorBlock, which can skip undesired video segments such as sponsored content.",
) {
dependsOn(
@ -164,7 +165,7 @@ val SponsorBlock = bytecodePatch(
// Cannot match using original immutable class because
// class may have been modified by other patches
seekbarMethod.immutableClassDef.seekbarOnDrawMethodMatch.let {
seekbarMethod.immutableClassDef.getSeekbarOnDrawMethodMatch().let {
it.method.apply {
// Set seekbar thickness.
val thicknessIndex = it[-1]

View file

@ -13,7 +13,8 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/announcements/AnnouncementsPatch;"
@Suppress("unused")
val Announcements = bytecodePatch(
val announcementsPatch = bytecodePatch(
name = "Announcements",
description = "Adds an option to show announcements from ReVanced on app startup.",
) {
dependsOn(

View file

@ -136,7 +136,9 @@ internal val BytecodePatchContext.imageEnumConstructorMethodMatch by composingFi
internal val BytecodePatchContext.setEnumMapMethodMatch by composingFirstMethod {
instructions(
ResourceType.DRAWABLE("yt_fill_bell_black_24"),
afterAtMost(10, method { toString() == "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;" }),
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;" },

View file

@ -113,7 +113,7 @@ internal val BytecodePatchContext.seekbarMethod by gettingFirstMethodDeclarative
/**
* Matches to _mutable_ class found in [seekbarMethod].
*/
internal val ClassDef.seekbarOnDrawMethodMatch by ClassDefComposing.composingFirstMethod {
internal fun ClassDef.getSeekbarOnDrawMethodMatch() = firstMethodComposite {
name("onDraw")
instructions(
method { toString() == "Ljava/lang/Math;->round(F)I" },

View file

@ -46,7 +46,8 @@ val disableVideoCodecsPatch = bytecodePatch(
method.replaceInstruction(
index,
"invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->" + "disableHdrVideo(Landroid/view/Display\$HdrCapabilities;)[I",
"invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->" +
$$"disableHdrVideo(Landroid/view/Display$HdrCapabilities;)[I",
)
},
),

View file

@ -18,6 +18,7 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.Opcode.*
import com.android.tools.smali.dexlib2.analysis.reflection.util.ReflectionUtils
import com.android.tools.smali.dexlib2.formatter.DexFormatter
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.*
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@ -27,8 +28,8 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.iface.value.*
import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.immutable.value.*
import com.android.tools.smali.dexlib2.util.MethodUtil
import java.util.*
import kotlin.collections.ArrayDeque
/**
* Starting from and including the instruction at index [startIndex],
@ -89,7 +90,7 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In
// This method is simple and does not follow branching.
throw IllegalArgumentException(
"Encountered a branch statement before " +
"a free register could be found from startIndex: $startIndex",
"a free register could be found from startIndex: $startIndex",
)
}
@ -109,7 +110,7 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In
// In practice this never occurs.
throw IllegalArgumentException(
"Could not find a free register from startIndex: " +
"$startIndex excluding: $registersToExclude",
"$startIndex excluding: $registersToExclude",
)
}
}
@ -193,7 +194,7 @@ private fun Method.findInstructionIndexFromToString(fieldName: String): Int {
val stringUsageIndex = indexOfFirstInstruction(stringIndex) {
val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/lang/StringBuilder;" &&
(this as? FiveRegisterInstruction)?.registerD == stringRegister
(this as? FiveRegisterInstruction)?.registerD == stringRegister
}
if (stringUsageIndex < 0) {
throw IllegalArgumentException("Could not find StringBuilder usage in: $this")
@ -345,7 +346,8 @@ fun MutableMethod.addInstructionsAtControlFlowLabel(
* @throws PatchException if the resource cannot be found.
* @see [indexOfFirstResourceIdOrThrow], [indexOfFirstLiteralInstructionReversed]
*/
fun Method.indexOfFirstResourceId(resourceName: String): Int = indexOfFirstLiteralInstruction(ResourceType.ID[resourceName])
fun Method.indexOfFirstResourceId(resourceName: String): Int =
indexOfFirstLiteralInstruction(ResourceType.ID[resourceName])
/**
* Get the index of the first instruction with the id of the given resource name or throw a [PatchException].
@ -458,7 +460,8 @@ fun Method.indexOfFirstLiteralInstructionReversedOrThrow(literal: Long): Int {
* @return the last literal instruction with the value, or -1 if not found.
* @see indexOfFirstLiteralInstructionOrThrow
*/
fun Method.indexOfFirstLiteralInstructionReversed(literal: Float) = indexOfFirstLiteralInstructionReversed(literal.toRawBits().toLong())
fun Method.indexOfFirstLiteralInstructionReversed(literal: Float) =
indexOfFirstLiteralInstructionReversed(literal.toRawBits().toLong())
/**
* Find the index of the last wide literal instruction with the given float value,
@ -478,7 +481,8 @@ fun Method.indexOfFirstLiteralInstructionReversedOrThrow(literal: Float): Int {
* @return the last literal instruction with the value, or -1 if not found.
* @see indexOfFirstLiteralInstructionOrThrow
*/
fun Method.indexOfFirstLiteralInstructionReversed(literal: Double) = indexOfFirstLiteralInstructionReversed(literal.toRawBits())
fun Method.indexOfFirstLiteralInstructionReversed(literal: Double) =
indexOfFirstLiteralInstructionReversed(literal.toRawBits())
/**
* Find the index of the last wide literal instruction with the given double value,
@ -550,9 +554,10 @@ fun Method.indexOfFirstInstruction(targetOpcode: Opcode): Int = indexOfFirstInst
* @return The index of the first opcode specified, or -1 if not found.
* @see indexOfFirstInstructionOrThrow
*/
fun Method.indexOfFirstInstruction(startIndex: Int = 0, targetOpcode: Opcode): Int = indexOfFirstInstruction(startIndex) {
opcode == targetOpcode
}
fun Method.indexOfFirstInstruction(startIndex: Int = 0, targetOpcode: Opcode): Int =
indexOfFirstInstruction(startIndex) {
opcode == targetOpcode
}
/**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
@ -587,9 +592,10 @@ fun Method.indexOfFirstInstructionOrThrow(targetOpcode: Opcode): Int = indexOfFi
* @throws PatchException
* @see indexOfFirstInstruction
*/
fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, targetOpcode: Opcode): Int = indexOfFirstInstructionOrThrow(startIndex) {
opcode == targetOpcode
}
fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, targetOpcode: Opcode): Int =
indexOfFirstInstructionOrThrow(startIndex) {
opcode == targetOpcode
}
/**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
@ -615,9 +621,10 @@ fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, filter: Instructi
* @return -1 if the instruction is not found.
* @see indexOfFirstInstructionReversedOrThrow
*/
fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, targetOpcode: Opcode): Int = indexOfFirstInstructionReversed(startIndex) {
opcode == targetOpcode
}
fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, targetOpcode: Opcode): Int =
indexOfFirstInstructionReversed(startIndex) {
opcode == targetOpcode
}
/**
* Get the index of matching instruction,
@ -654,9 +661,10 @@ fun Method.indexOfFirstInstructionReversed(targetOpcode: Opcode): Int = indexOfF
* @return The index of the instruction.
* @see indexOfFirstInstructionReversed
*/
fun Method.indexOfFirstInstructionReversedOrThrow(startIndex: Int? = null, targetOpcode: Opcode): Int = indexOfFirstInstructionReversedOrThrow(startIndex) {
opcode == targetOpcode
}
fun Method.indexOfFirstInstructionReversedOrThrow(startIndex: Int? = null, targetOpcode: Opcode): Int =
indexOfFirstInstructionReversedOrThrow(startIndex) {
opcode == targetOpcode
}
/**
* Get the index of matching instruction,
@ -713,7 +721,8 @@ fun Method.findInstructionIndicesReversedOrThrow(filter: Instruction.() -> Boole
* _Returns an empty list if no indices are found_
* @see findInstructionIndicesReversedOrThrow
*/
fun Method.findInstructionIndicesReversed(opcode: Opcode): List<Int> = findInstructionIndicesReversed { this.opcode == opcode }
fun Method.findInstructionIndicesReversed(opcode: Opcode): List<Int> =
findInstructionIndicesReversed { this.opcode == opcode }
/**
* @return An immutable list of indices of the opcode in reverse order.
@ -779,33 +788,30 @@ internal fun MutableMethod.insertLiteralOverride(literalIndex: Int, override: Bo
/**
* Iterates all instructions as sequence in all methods of all classes in the [BytecodePatchContext].
* The instructions are provided in reverse order (last to first).
* The [block] is invoked after collecting all instructions to avoid concurrent modification issues.
*
* @param match A function that matches instructions. If a match is found, it returns a value of type [T], otherwise null.
* @param transform A function that transforms the matched instruction using the mutable method and the matched value
* of type [T].
*/
fun BytecodePatchContext.forEachInstructionAsSequence(
block: (classDef: MutableClassDef, method: MutableMethod, matchingIndex: Int, instruction: Instruction) -> Unit,
fun <T> BytecodePatchContext.forEachInstructionAsSequence(
match: (classDef: ClassDef, method: Method, instruction: Instruction, index: Int) -> T?,
transform: (MutableMethod, T) -> Unit
) {
classDefs.asSequence().flatMap { classDef ->
val mutableClassDef by lazy { classDefs.getOrReplaceMutable(classDef) }
classDefs.flatMap { classDef ->
classDef.methods.mapNotNull { method ->
val matches = method.instructionsOrNull?.mapIndexedNotNull { index, instruction ->
match(classDef, method, instruction, index)
} ?: return@mapNotNull null
classDef.methods.asSequence().flatMap { method ->
val instructions =
method.instructionsOrNull as? List<Instruction> ?: return@flatMap emptySequence<() -> Unit>()
val mutableMethod by lazy { mutableClassDef.firstMutableMethod(method) }
instructions.asReversed().asSequence().mapIndexed { index, instruction ->
{
block(
mutableClassDef,
mutableMethod,
instructions.size - 1 - index,
instruction,
)
} // Reverse indices again.
}
if (matches.any()) method to matches else null
}
}.forEach { it() }
}.forEach { (method, matches) ->
val method = firstMutableMethod(method)
val matches = matches.toCollection(ArrayDeque())
while (!matches.isEmpty()) transform(method, matches.removeLast())
}
}
private fun MutableMethod.checkReturnType(expectedTypes: Iterable<Class<*>>) {