suppress and other migratios

This commit is contained in:
oSumAtrIX
2026-01-24 19:55:54 +01:00
parent d6a437c7b1
commit 230ab2fa59
52 changed files with 257 additions and 283 deletions

View File

@@ -9,23 +9,24 @@ import app.revanced.util.getNode
import org.w3c.dom.Element
import java.io.File
@Suppress("unused", "ObjectPropertyName")
val `Custom network security` = creatingResourcePatch(
description = "Allows trusting custom certificate authorities for a specific domain.",
use = false
use = false,
) {
val targetDomains by stringsOption(
name = "Target domains",
description = "List of domains to which the custom trust configuration will be applied (one domain per entry).",
default = listOf("example.com"),
required = true
required = true,
)
val includeSubdomains by booleanOption(
name = "Include subdomains",
description = "Applies the configuration to all subdomains of the target domains.",
default = false,
required = true
required = true,
)
val customCAFilePaths by stringsOption(
@@ -40,35 +41,35 @@ val `Custom network security` = creatingResourcePatch(
CA files will be bundled in res/raw/ of resulting APK
""".trimIndentMultiline(),
default = null,
required = false
required = false,
)
val allowUserCerts by booleanOption(
name = "Trust user added CAs",
description = "Makes an app trust certificates from the Android user store for the specified domains, and if the option \"Include Subdomains\" is enabled then also the subdomains.",
default = false,
required = true
required = true,
)
val allowSystemCerts by booleanOption(
name = "Trust system CAs",
description = "Makes an app trust certificates from the Android system store for the specified domains, and and if the option \"Include Subdomains\" is enabled then also the subdomains.",
default = true,
required = true
required = true,
)
val allowCleartextTraffic by booleanOption(
name = "Allow cleartext traffic (HTTP)",
description = "Allows unencrypted HTTP traffic for the specified domains, and if \"Include Subdomains\" is enabled then also the subdomains.",
default = false,
required = true
required = true,
)
val overridePins by booleanOption(
name = "Override certificate pinning",
description = "Overrides certificate pinning for the specified domains and their subdomains if the option \"Include Subdomains\" is enabled to allow inspecting app traffic via a proxy.",
default = false,
required = true
required = true,
)
fun generateNetworkSecurityConfig(): String {
@@ -113,55 +114,47 @@ ${trustAnchorsXML.trimEnd()}
</trust-anchors>
</domain-config>
</network-security-config>
""".trimIndent()
""".trimIndent()
}
apply {
val nscFileNameBare = "network_security_config"
val resXmlDir = "res/xml"
val resRawDir = "res/raw"
val nscFileNameWithSuffix = "$nscFileNameBare.xml"
document("AndroidManifest.xml").use { document ->
val applicationNode = document.getNode("application") as Element
applicationNode.setAttribute("android:networkSecurityConfig", "@xml/$nscFileNameBare")
}
File(get(resXmlDir), nscFileNameWithSuffix).apply {
writeText(generateNetworkSecurityConfig())
}
for (customCAFilePath in customCAFilePaths ?: emptyList()) {
val file = File(customCAFilePath)
if (!file.exists()) {
throw PatchException(
"The custom CA file path cannot be found: " +
file.absolutePath
file.absolutePath,
)
}
if (!file.isFile) {
throw PatchException(
"The custom CA file path must be a file: "
+ file.absolutePath
"The custom CA file path must be a file: " +
file.absolutePath,
)
}
val caFileNameWithoutSuffix = customCAFilePath.substringAfterLast('/').substringBefore('.')
val caFile = File(customCAFilePath)
File(
get(resRawDir),
caFileNameWithoutSuffix
caFileNameWithoutSuffix,
).writeText(
caFile.readText()
caFile.readText(),
)
}
}
}

View File

@@ -26,9 +26,10 @@ fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
}
}
@Suppress("ObjectPropertyName")
val `Change package name` = creatingResourcePatch(
description = "Appends \".revanced\" to the package name by default. " +
"Changing the package name of the app can lead to unexpected issues.",
"Changing the package name of the app can lead to unexpected issues.",
use = false,
) {
packageNameOption = stringOption(
@@ -45,14 +46,14 @@ val `Change package name` = creatingResourcePatch(
default = false,
name = "Update permissions",
description = "Update compatibility receiver permissions. " +
"Enabling this can fix installation errors, but this can also break features in certain apps.",
"Enabling this can fix installation errors, but this can also break features in certain apps.",
)
val updateProviders by booleanOption(
default = false,
name = "Update providers",
description = "Update provider names declared by the app. " +
"Enabling this can fix installation errors, but this can also break features in certain apps.",
"Enabling this can fix installation errors, but this can also break features in certain apps.",
)
afterDependents {

View File

@@ -3,6 +3,7 @@ package app.revanced.patches.mifitness.misc.login
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.patch.creatingBytecodePatch
@Suppress("ObjectPropertyName")
val `Fix login` by creatingBytecodePatch(
description = "Fixes login for uncertified Mi Fitness app",
) {

View File

@@ -5,6 +5,7 @@ import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.util.returnEarly
@Suppress("unused", "ObjectPropertyName")
val `Remove background playback restrictions` by creatingBytecodePatch(
description = "Removes restrictions on background playback, including playing kids videos in the background.",
) {

View File

@@ -1,9 +1,12 @@
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
@@ -16,6 +19,7 @@ internal val BytecodePatchContext.lithoOnBoundsChangeMethod by gettingFirstMetho
returnType("V")
parameterTypes("Landroid/graphics/Rect;")
instructions(
allOf(Opcode.IPUT_OBJECT(), field { definingClass type == "Landroid/graphics/Path;" }),
fieldAccess(
opcode = Opcode.IPUT_OBJECT,
definingClass = "this",

View File

@@ -7,22 +7,22 @@ import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
internal val BytecodePatchContext.googlePlayUtilityMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.googlePlayUtilityMethod by gettingFirstMethodDeclaratively(
"This should never happen.",
"MetadataValueReader",
"com.google.android.gms",
) {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returnType("I")
parameterTypes("L", "I")
strings(
"This should never happen.",
"MetadataValueReader",
"com.google.android.gms",
)
}
internal val BytecodePatchContext.serviceCheckMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.serviceCheckMethod by gettingFirstMethodDeclaratively(
"Google Play Services not available",
) {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returnType("V")
parameterTypes("L", "I")
strings("Google Play Services not available")
}
internal val BytecodePatchContext.gmsCoreSupportMethod by gettingFirstMethodDeclaratively {

View File

@@ -1,22 +1,24 @@
package app.revanced.patches.tiktok.misc.settings
internal val BytecodePatchContext.addSettingsEntryMethod by gettingFirstMethodDeclaratively {
custom { method, classDef ->
classDef.endsWith("/SettingNewVersionFragment;") &&
method.name == "initUnitManger"
}
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal val BytecodePatchContext.addSettingsEntryMethod by gettingFirstMutableMethodDeclaratively {
name("initUnitManger")
definingClass { endsWith("/SettingNewVersionFragment;") }
}
internal val BytecodePatchContext.adPersonalizationActivityOnCreateMethod by gettingFirstMethodDeclaratively {
custom { method, classDef ->
classDef.endsWith("/AdPersonalizationActivity;") &&
method.name == "onCreate"
}
internal val BytecodePatchContext.adPersonalizationActivityOnCreateMethod by gettingFirstMutableMethodDeclaratively {
name("onCreate")
definingClass { endsWith("/AdPersonalizationActivity;") }
}
internal val BytecodePatchContext.settingsEntryMethod by gettingFirstMethodDeclaratively {
strings("pls pass item or extends the EventUnit")
}
internal val BytecodePatchContext.settingsEntryMethod by gettingFirstMethodDeclaratively(
"pls pass item or extends the EventUnit",
)
internal val BytecodePatchContext.settingsEntryInfoMethod by gettingFirstMethodDeclaratively {
strings(

View File

@@ -1,16 +1,13 @@
package app.revanced.patches.tiktok.misc.settings
import app.revanced.patcher.extensions.ExternalLabel
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.*
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patches.shared.layout.branding.addBrandLicensePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tiktok/settings/TikTokActivityHook;"
@@ -37,16 +34,16 @@ val Settings by creatingBytecodePatch(
"Ljava/lang/String;" +
")Ljava/lang/Object;"
fun String.toClassName(): String = substring(1, this.length - 1).replace("/", ".")
fun String.toClassName() = substring(1, this.length - 1).replace("/", ".")
// Find the class name of classes which construct a settings entry
val settingsButtonClass = settingsEntryMethod.originalClassDef.type.toClassName()
val settingsButtonInfoClass = settingsEntryInfoMethod.originalClassDef.type.toClassName()
val settingsButtonClass = settingsEntryMethod.immutableClassDef.type.toClassName()
val settingsButtonInfoClass = settingsEntryInfoMethod.immutableClassDef.type.toClassName()
// Create a settings entry for 'revanced settings' and add it to settings fragment
addSettingsEntryMethod.apply {
val markIndex = implementation!!.instructions.indexOfFirst {
it.opcode == Opcode.IGET_OBJECT && ((it as Instruction22c).reference as FieldReference).name == "headerUnit"
val markIndex = indexOfFirstInstruction {
opcode == Opcode.IGET_OBJECT && fieldReference?.name == "headerUnit"
}
val getUnitManager = getInstruction(markIndex + 2)
@@ -67,24 +64,22 @@ val Settings by creatingBytecodePatch(
const-string v1, "$settingsButtonInfoClass"
invoke-static {v0, v1}, $createSettingsEntryMethodDescriptor
move-result-object v0
check-cast v0, ${settingsEntryMethod.originalClassDef.type}
check-cast v0, ${settingsEntryMethod.immutableClassDef.type}
""",
)
}
// Initialize the settings menu once the replaced setting entry is clicked.
adPersonalizationActivityOnCreateMethod.apply {
val initializeSettingsIndex = implementation!!.instructions.indexOfFirst {
it.opcode == Opcode.INVOKE_SUPER
} + 1
val initializeSettingsIndex = indexOfFirstInstruction(Opcode.INVOKE_SUPER) + 1
val thisRegister = getInstruction<Instruction35c>(initializeSettingsIndex - 1).registerC
val thisRegister = getInstruction<FiveRegisterInstruction>(initializeSettingsIndex - 1).registerC
val usableRegister = implementation!!.registerCount - parameters.size - 2
addInstructionsWithLabels(
initializeSettingsIndex,
"""
invoke-static {v$thisRegister}, $initializeSettingsMethodDescriptor
invoke-static { v$thisRegister }, $initializeSettingsMethodDescriptor
move-result v$usableRegister
if-eqz v$usableRegister, :do_not_open
return-void

View File

@@ -11,6 +11,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
@Suppress("unused", "ObjectPropertyName")
val `Block audio ads` by creatingBytecodePatch(
description = "Blocks audio ads in streams and VODs.",
) {

View File

@@ -9,6 +9,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
@Suppress("unused", "ObjectPropertyName")
val `Block embedded ads` by creatingBytecodePatch(
description = "Blocks embedded stream ads using services like Luminous or PurpleAdBlocker.",
) {

View File

@@ -14,6 +14,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
@Suppress("ObjectPropertyName")
val `Block video ads` by creatingBytecodePatch(
description = "Blocks video ads in streams and VODs.",
) {

View File

@@ -12,6 +12,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
@Suppress("unused", "ObjectPropertyName")
val `Show deleted messages` by creatingBytecodePatch(
description = "Shows deleted chat messages behind a clickable spoiler.",
) {
@@ -33,7 +34,7 @@ val `Show deleted messages` by creatingBytecodePatch(
addResources("twitch", "chat.antidelete.showDeletedMessagesPatch")
PreferenceScreen.CHAT.GENERAL.addPreferences(
ListPreference("revanced_show_deleted_messages")
ListPreference("revanced_show_deleted_messages"),
)
// Spoiler mode: Force set hasModAccess member to true in constructor

View File

@@ -11,6 +11,7 @@ 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
@Suppress("unused", "ObjectPropertyName")
val `Auto claim channel points` by creatingBytecodePatch(
description = "Automatically claim Channel Points.",
) {

View File

@@ -9,6 +9,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings
@Suppress("ObjectPropertyName")
val `Debug mode` by creatingBytecodePatch(
description = "Enables Twitch's internal debugging mode.",
use = false,

View File

@@ -1,9 +1,11 @@
package app.revanced.patches.twitch.misc.settings
import app.revanced.patcher.classDef
import app.revanced.patcher.extensions.ExternalLabel
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -13,6 +15,7 @@ import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.mutable.MutableField.Companion.toMutable
import com.android.tools.smali.dexlib2.mutable.MutableMethod
private const val REVANCED_SETTINGS_MENU_ITEM_NAME = "RevancedSettings"
private const val REVANCED_SETTINGS_MENU_ITEM_ID = 0x7
@@ -68,23 +71,22 @@ val Settings by creatingBytecodePatch(
)
// Hook onCreate to handle fragment creation.
val insertIndex = settingsActivityOnCreateFingerprint.method.implementation!!.instructions.size - 2
settingsActivityOnCreateFingerprint.method.addInstructionsWithLabels(
insertIndex,
"""
invoke-static { p0 }, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingsCreation(Landroidx/appcompat/app/AppCompatActivity;)Z
move-result v0
if-eqz v0, :no_rv_settings_init
return-void
""",
ExternalLabel(
"no_rv_settings_init",
settingsActivityOnCreateFingerprint.method.getInstruction(insertIndex),
),
)
settingsActivityOnCreateMethod.apply {
val insertIndex = instructions.size - 2
addInstructionsWithLabels(
insertIndex,
"""
invoke-static { p0 }, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingsCreation(Landroidx/appcompat/app/AppCompatActivity;)Z
move-result v0
if-eqz v0, :no_rv_settings_init
return-void
""",
ExternalLabel("no_rv_settings_init", getInstruction(insertIndex)),
)
}
// Create new menu item for settings menu.
fun Fingerprint.injectMenuItem(
fun MutableMethod.injectMenuItem(
name: String,
value: Int,
titleResourceName: String,
@@ -93,7 +95,7 @@ val Settings by creatingBytecodePatch(
// Add new static enum member field
classDef.staticFields.add(
ImmutableField(
method.definingClass,
definingClass,
name,
MENU_ITEM_ENUM_CLASS_DESCRIPTOR,
AccessFlags.PUBLIC.value or
@@ -107,8 +109,8 @@ val Settings by creatingBytecodePatch(
)
// Add initializer for the new enum member
method.addInstructions(
method.implementation!!.instructions.size - 4,
addInstructions(
instructions.size - 4,
"""
new-instance v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR
const-string v1, "$titleResourceName"
@@ -125,7 +127,7 @@ val Settings by creatingBytecodePatch(
)
}
settingsMenuItemEnumFingerprint.injectMenuItem(
settingsMenuItemEnumMethod.injectMenuItem(
REVANCED_SETTINGS_MENU_ITEM_NAME,
REVANCED_SETTINGS_MENU_ITEM_ID,
REVANCED_SETTINGS_MENU_ITEM_TITLE_RES,
@@ -133,7 +135,7 @@ val Settings by creatingBytecodePatch(
)
// Intercept settings menu creation and add new menu item.
menuGroupsUpdatedFingerprint.method.addInstructions(
menuGroupsUpdatedMethod.addInstructions(
0,
"""
sget-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$REVANCED_SETTINGS_MENU_ITEM_NAME:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR

View File

@@ -11,6 +11,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
@Suppress("ObjectPropertyName")
val `Video ads` by creatingBytecodePatch(
description = "Adds an option to remove ads in the video player.",
) {

View File

@@ -14,6 +14,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Remove viewer discretion dialog` by creatingBytecodePatch(
description = "Adds an option to remove the dialog that appears when opening a video that has been age-restricted " +
"by accepting it automatically. This does not bypass the age restriction.",

View File

@@ -3,6 +3,9 @@ package app.revanced.patches.youtube.interaction.seekbar
import app.revanced.patcher.accessFlags
import app.revanced.patcher.after
import app.revanced.patcher.afterAtMost
import app.revanced.patcher.allOf
import app.revanced.patcher.custom
import app.revanced.patcher.field
import app.revanced.patcher.fieldAccess
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.firstMutableMethodDeclaratively
@@ -10,13 +13,16 @@ import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.method
import app.revanced.patcher.methodCall
import app.revanced.patcher.name
import app.revanced.patcher.newInstance
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 app.revanced.patcher.type
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_47_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_19_or_greater
@@ -126,24 +132,22 @@ internal val BytecodePatchContext.onTouchEventHandlerMethod by gettingFirstMetho
}
internal val BytecodePatchContext.seekbarTappingMethod by gettingFirstMethodDeclaratively {
name("onTouchEvent")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Z")
parameterTypes("Landroid/view/MotionEvent;")
instructions(
Int.MAX_VALUE(),
newInstance("Landroid/graphics/Point;"),
methodCall(smali = "Landroid/graphics/Point;-><init>(II)V", after()),
Int.MAX_VALUE.toLong()(),
allOf(Opcode.NEW_INSTANCE, type("Landroid/graphics/Point;")),
after(method { toString() == "Landroid/graphics/Point;-><init>(II)V" }),
methodCall(
smali = "Lj\$/util/Optional;->of(Ljava/lang/Object;)Lj\$/util/Optional;",
after(),
),
after(Opcode.MOVE_RESULT_OBJECT()),
fieldAccess(opcode = Opcode.IPUT_OBJECT, type = "Lj\$/util/Optional;", after()),
after(allOf(Opcode.IPUT_OBJECT(), field { type == "Lj\$/util/Optional;" })),
afterAtMost(10, Opcode.INVOKE_VIRTUAL()),
)
custom { method, _ -> method.name == "onTouchEvent" }
}
internal val BytecodePatchContext.slideToSeekMethod by gettingFirstMethodDeclaratively {

View File

@@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Disable auto captions` by creatingBytecodePatch(
description = "Adds an option to disable captions from being automatically enabled.",
) {

View File

@@ -13,6 +13,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import java.util.logging.Logger
@Suppress("unused", "ObjectPropertyName")
val `Hide video action buttons` by creatingResourcePatch(
description = "Adds options to hide action buttons (such as the Download button) under videos.",
) {
@@ -29,7 +30,7 @@ val `Hide video action buttons` by creatingResourcePatch(
"20.14.43",
"20.21.37",
// 20.22+ does not yet support hiding all player buttons.
)
),
)
apply {
@@ -48,9 +49,9 @@ val `Hide video action buttons` by creatingResourcePatch(
// the buffer is the same for all buttons.
Logger.getLogger(this::class.java.name).warning(
"\n!!!" +
"\n!!! Not all player action buttons can be set hidden when patching 20.22+" +
"\n!!! Patch 20.21.37 or lower if you want to hide player action buttons" +
"\n!!!"
"\n!!! Not all player action buttons can be set hidden when patching 20.22+" +
"\n!!! Patch 20.21.37 or lower if you want to hide player action buttons" +
"\n!!!",
)
} else {
preferences.addAll(
@@ -65,15 +66,15 @@ val `Hide video action buttons` by creatingResourcePatch(
SwitchPreference("revanced_hide_shop_button"),
SwitchPreference("revanced_hide_stop_ads_button"),
SwitchPreference("revanced_hide_thanks_button"),
)
),
)
}
PreferenceScreen.PLAYER.addPreferences(
PreferenceScreenPreference(
"revanced_hide_buttons_screen",
preferences = preferences
)
preferences = preferences,
),
)
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;")

View File

@@ -1,23 +1,23 @@
package app.revanced.patches.youtube.layout.buttons.navigation
import app.revanced.patcher.accessFlags
import app.revanced.patcher.addString
import app.revanced.patcher.after
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.methodCall
import app.revanced.patcher.opcode
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.addCreateButtonViewMethod by gettingFirstMethodDeclaratively {
internal val addCreateButtonViewMethodMatch = firstMethodComposite {
instructions(
"Android Wear"(),
Opcode.IF_EQZ(),
addString("Android Automotive", after()),
after("Android Automotive"()),
)
}

View File

@@ -24,6 +24,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;"
@Suppress("ObjectPropertyName")
val `Navigation buttons` by creatingBytecodePatch(
description = "Adds options to hide and change navigation buttons (such as the Shorts button).",
) {
@@ -81,20 +82,18 @@ val `Navigation buttons` by creatingBytecodePatch(
)
// Switch create with notifications button.
addCreateButtonViewMethod.let {
it.method.apply {
val conditionalCheckIndex = it.instructionMatches[1].index
val conditionRegister =
getInstruction<OneRegisterInstruction>(conditionalCheckIndex).registerA
addCreateButtonViewMethodMatch.method.apply {
val conditionalCheckIndex = addCreateButtonViewMethodMatch.indices[1]
val conditionRegister =
getInstruction<OneRegisterInstruction>(conditionalCheckIndex).registerA
addInstructions(
conditionalCheckIndex,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z
move-result v$conditionRegister
""",
)
}
addInstructions(
conditionalCheckIndex,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z
move-result v$conditionRegister
""",
)
}
// Hide navigation button labels.

View File

@@ -17,6 +17,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;"
@Suppress("unused", "ObjectPropertyName")
val `Disable fullscreen ambient mode` by creatingBytecodePatch(
description = "Adds an option to disable the ambient mode when in fullscreen.",
) {

View File

@@ -79,6 +79,7 @@ private const val CUSTOM_FILTER_CLASS_NAME =
private const val KEYWORD_FILTER_CLASS_NAME =
"Lapp/revanced/extension/youtube/patches/components/KeywordContentFilter;"
@Suppress("unused", "ObjectPropertyName")
val `Hide layout components` by creatingBytecodePatch(
description = "Adds options to hide general layout components.",

View File

@@ -11,6 +11,7 @@ import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
@Suppress("unused", "ObjectPropertyName")
val `Hide player flyout menu items` by creatingBytecodePatch(
description = "Adds options to hide menu items that appear when pressing the gear icon in the video player.",
) {
@@ -27,7 +28,7 @@ val `Hide player flyout menu items` by creatingBytecodePatch(
"20.14.43",
"20.21.37",
"20.31.40",
)
),
)
apply {
@@ -50,7 +51,7 @@ val `Hide player flyout menu items` by creatingBytecodePatch(
SwitchPreference("revanced_hide_player_flyout_lock_screen"),
SwitchPreference(
key = "revanced_hide_player_flyout_audio_track",
tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference"
tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference",
),
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
SwitchPreference("revanced_hide_player_flyout_sleep_timer"),

View File

@@ -17,6 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableRollingNumberAnimationsPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Disable rolling number animations` by creatingBytecodePatch(
description = "Adds an option to disable rolling number animations of video view count, user likes, and upload time.",
) {

View File

@@ -13,6 +13,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableSignInToTvPopupPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Disable sign in to TV popup` by creatingBytecodePatch(
description = "Adds an option to disable the popup asking to sign into a TV on the same local network.",
) {

View File

@@ -1,16 +1,7 @@
package app.revanced.patches.youtube.layout.hide.time
import app.revanced.patcher.accessFlags
import app.revanced.patcher.after
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.opcode
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -20,20 +11,11 @@ internal val BytecodePatchContext.timeCounterMethod by gettingFirstMethodDeclara
parameterTypes()
instructions(
Opcode.SUB_LONG_2ADDR(),
methodCall(
opcode = Opcode.INVOKE_STATIC,
returnType = "Ljava/lang/CharSequence;",
after(),
),
after(allOf(Opcode.INVOKE_STATIC(), method { returnType == "Ljava/lang/CharSequence;" })),
after(Opcode.MOVE_RESULT_OBJECT()),
fieldAccess(opcode = Opcode.IGET_WIDE, type = "J", after()),
fieldAccess(opcode = Opcode.IGET_WIDE, type = "J", after()),
after(allOf(Opcode.IGET_WIDE(), field { type == "J" })),
after(allOf(Opcode.IGET_WIDE(), field { type == "J" })),
after(Opcode.SUB_LONG_2ADDR()),
methodCall(
opcode = Opcode.INVOKE_STATIC,
returnType = "Ljava/lang/CharSequence;",
afterAtMost(5),
),
afterAtMost(5, allOf(Opcode.INVOKE_STATIC(), method { returnType == "Ljava/lang/CharSequence;" })),
)
}

View File

@@ -11,6 +11,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideTimestampPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Hide timestamp` by creatingBytecodePatch(
description = "Adds an option to hide the timestamp in the bottom left of the video player.",
) {

View File

@@ -1,9 +1,12 @@
package app.revanced.patches.youtube.layout.panels.popup
internal val BytecodePatchContext.engagementPanelControllerMethod by gettingFirstMethodDeclaratively {
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val BytecodePatchContext.engagementPanelControllerMethod by gettingFirstMutableMethodDeclaratively(
"EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.",
"[EngagementPanel] Cannot show EngagementPanel before EngagementPanelController.init() has been called.",
) {
returnType("L")
strings(
"EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.",
"[EngagementPanel] Cannot show EngagementPanel before EngagementPanelController.init() has been called.",
)
}

View File

@@ -11,6 +11,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisablePlayerPopupPanelsPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Disable player popup panels` by creatingBytecodePatch(
description = "Adds an option to disable panels (such as live chat) from opening automatically.",
) {

View File

@@ -1,17 +1,7 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.accessFlags
import app.revanced.patcher.after
import app.revanced.patcher.afterAtMost
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.literal
import app.revanced.patcher.opcode
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
@@ -24,7 +14,7 @@ internal val BytecodePatchContext.openVideosFullscreenPortraitMethod by gettingF
instructions(
Opcode.MOVE_RESULT(), // Conditional check to modify.
// Open videos fullscreen portrait feature flag.
literal(45666112L, afterAtMost(5)), // Cannot be more than 5.
afterAtMost(5, 45666112L()), // Cannot be more than 5.
afterAtMost(10, Opcode.MOVE_RESULT()),
)
}
@@ -51,11 +41,10 @@ internal val BytecodePatchContext.openVideosFullscreenPortraitLegacyMethod by ge
)
}
internal val BytecodePatchContext.openVideosFullscreenHookPatchExtensionMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.openVideosFullscreenHookPatchExtensionMethod by gettingFirstMutableMethodDeclaratively {
name("isFullScreenPatchIncluded")
definingClass(EXTENSION_CLASS_DESCRIPTOR)
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returnType("Z")
parameterTypes()
custom { methodDef, classDef ->
methodDef.name == "isFullScreenPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}

View File

@@ -40,6 +40,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter;"
@Suppress("ObjectPropertyName")
val `Return YouTube Dislike` by creatingBytecodePatch(
description = "Adds an option to show the dislike count of videos with Return YouTube Dislike.",
) {

View File

@@ -24,6 +24,7 @@ import java.util.logging.Logger
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/WideSearchbarPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Wide search bar` by creatingBytecodePatch(
description = "Adds an option to replace the search icon with a wide search bar. " +
"This will hide the YouTube logo when active.",

View File

@@ -1,10 +1,13 @@
package app.revanced.patches.youtube.layout.shortsautoplay
import app.revanced.patcher.accessFlags
import app.revanced.patcher.addString
import app.revanced.patcher.fieldAccess
import app.revanced.patcher.afterAtMost
import app.revanced.patcher.field
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.method
import app.revanced.patcher.methodCall
import app.revanced.patcher.opcode
import app.revanced.patcher.parameterTypes
@@ -13,7 +16,7 @@ import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val BytecodePatchContext.reelEnumConstructorMethod by gettingFirstMethodDeclaratively {
internal val reelEnumConstructorMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
instructions(
"REEL_LOOP_BEHAVIOR_UNKNOWN"(),
@@ -38,24 +41,23 @@ internal val BytecodePatchContext.reelPlaybackRepeatParentMethod by gettingFirst
internal val BytecodePatchContext.reelPlaybackRepeatMethod by gettingFirstMethodDeclaratively {
returnType("V")
parameterTypes("L")
instructions(
methodCall(smali = "Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z"),
)
instructions(method { toString() == "Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z" })
}
internal val BytecodePatchContext.reelPlaybackMethod by gettingFirstMethodDeclaratively {
internal val reelPlaybackMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("J")
returnType("V")
val methodParametersPrefix = listOf("I", "L", "L")
instructions(
fieldAccess(
definingClass = "Ljava/util/concurrent/TimeUnit;",
name = "MILLISECONDS",
),
methodCall(
name = "<init>",
parameters = listOf("I", "L", "L"),
afterAtMost(15),
field { definingClass == "Ljava/util/concurrent/TimeUnit;" && name == "MILLISECONDS" },
afterAtMost(
15,
method {
name == "<init>" &&
parameterTypes.zip(methodParametersPrefix).all { (a, b) -> a.startsWith(b) }
},
),
methodCall(
opcode = Opcode.INVOKE_VIRTUAL,

View File

@@ -4,6 +4,8 @@ import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.methodReference
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.creatingBytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
@@ -72,11 +74,11 @@ val `Shorts autoplay` by creatingBytecodePatch(
var reelEnumClass: String
reelEnumConstructorMethod.let {
reelEnumClass = it.originalClassDef.type
reelEnumConstructorMethodMatch.apply {
reelEnumClass = immutableClassDef.type
it.method.addInstructions(
it.indices.last(),
method.addInstructions(
indices.last(),
"""
# Pass the first enum value to extension.
# Any enum value of this type will work.
@@ -87,7 +89,7 @@ val `Shorts autoplay` by creatingBytecodePatch(
}
reelPlaybackRepeatMethod.match(
reelPlaybackRepeatParentMethod.originalClassDef,
reelPlaybackRepeatParentMethod.immutableClassDef,
).method.apply {
// The behavior enums are looked up from an ordinal value to an enum type.
findInstructionIndicesReversedOrThrow {
@@ -112,10 +114,10 @@ val `Shorts autoplay` by creatingBytecodePatch(
// Manually restore the removed 'Autoplay' code.
if (is_20_09_or_greater) {
// Variable names are only a rough guess of what these methods do.
val userActionMethodReference = reelPlaybackMethod.instructionMatches[1]
.getInstruction<ReferenceInstruction>().reference as MethodReference
val reelSequenceControllerMethodReference = reelPlaybackMethod.instructionMatches[2]
.getInstruction<ReferenceInstruction>().reference as MethodReference
val userActionMethodReference =
reelPlaybackMethodMatch.method.getInstruction(reelPlaybackMethodMatch.indices[1]).methodReference
val reelSequenceControllerMethodReference =
reelPlaybackMethodMatch.method.getInstruction(reelPlaybackMethodMatch.indices[2]).methodReference
reelPlaybackRepeatMethod.apply {
// Find the first call modified by extension code above.

View File

@@ -1,15 +1,7 @@
package app.revanced.patches.youtube.layout.spoofappversion
import app.revanced.patcher.accessFlags
import app.revanced.patcher.fieldAccess
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.methodCall
import app.revanced.patcher.opcode
import app.revanced.patcher.opcodes
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import app.revanced.patches.shared.misc.mapping.ResourceType
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -19,21 +11,22 @@ internal val BytecodePatchContext.toolBarButtonMethod by gettingFirstMethodDecla
returnType("V")
instructions(
ResourceType.ID("menu_item_view"),
methodCall(returnType = "I", opcode = Opcode.INVOKE_INTERFACE),
allOf(Opcode.INVOKE_VIRTUAL(), method { returnType == "I" }),
after(Opcode.MOVE_RESULT()),
fieldAccess(type = "Landroid/widget/ImageView;", opcode = Opcode.IGET_OBJECT, afterAtMost(6)),
methodCall("Landroid/content/res/Resources;", "getDrawable", afterAtMost(8)),
methodCall("Landroid/widget/ImageView;", "setImageDrawable", afterAtMost(4)),
afterAtMost(6, allOf(Opcode.IGET_OBJECT(), field { type == "Landroid/widget/ImageView;" })),
afterAtMost(8, method { name == "getDrawable" && definingClass == "Landroid/content/res/Resources;" }),
afterAtMost(4, method { name == "setImageDrawable" && definingClass == "Landroid/widget/ImageView;" }),
)
custom { method, _ ->
// 20.37+ has second parameter of "Landroid/content/Context;"
val parameterCount = method.parameterTypes.count()
(parameterCount == 1 || parameterCount == 2) &&
method.parameterTypes.firstOrNull() == "Landroid/view/MenuItem;"
}
// 20.37+ has second parameter of "Landroid/content/Context;"
custom { parameterTypes.count() in 1..2 && parameterTypes.first() == "Landroid/view/MenuItem;" }
}
internal val BytecodePatchContext.spoofAppVersionMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.spoofAppVersionMethod by gettingFirstMethodDeclaratively(
// Instead of applying a bytecode patch, it might be possible to only rely on code from the extension and
// manually set the desired version string as this keyed value in the SharedPreferences.
// But, this bytecode patch is simple and it works.
"pref_override_build_version_name",
) {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returnType("L")
parameterTypes("L")
@@ -42,8 +35,4 @@ internal val BytecodePatchContext.spoofAppVersionMethod by gettingFirstMethodDec
Opcode.GOTO,
Opcode.CONST_STRING,
)
// Instead of applying a bytecode patch, it might be possible to only rely on code from the extension and
// manually set the desired version string as this keyed value in the SharedPreferences.
// But, this bytecode patch is simple and it works.
strings("pref_override_build_version_name")
}

View File

@@ -23,6 +23,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch;"
@Suppress("ObjectPropertyName")
val `Spoof app version` by creatingBytecodePatch(
description = "Adds an option to trick YouTube into thinking you are running an older version of the app. " +
"This can be used to restore old UI elements and features.",

View File

@@ -17,6 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeStartPagePatch;"
@Suppress("unused", "ObjectPropertyName")
val `Change start page` by creatingBytecodePatch(
description = "Adds an option to set which page the app opens in instead of the homepage.",
) {

View File

@@ -21,6 +21,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableResumingStartupShortsPlayerPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Disable resuming Shorts on startup` by creatingBytecodePatch(
description = "Adds an option to disable the Shorts player from resuming on app startup when Shorts were last being watched.",
) {

View File

@@ -19,6 +19,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/AlternativeThumbnailsPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Alternative thumbnails` by creatingBytecodePatch(
description = "Adds options to replace video thumbnails using the DeArrow API or image captures from the video.",
) {
@@ -36,7 +37,7 @@ val `Alternative thumbnails` by creatingBytecodePatch(
"20.14.43",
"20.21.37",
"20.31.40",
)
),
)
apply {
@@ -48,27 +49,27 @@ val `Alternative thumbnails` by creatingBytecodePatch(
ListPreference(
key = "revanced_alt_thumbnail_home",
entriesKey = entries,
entryValuesKey = values
entryValuesKey = values,
),
ListPreference(
key = "revanced_alt_thumbnail_subscription",
entriesKey = entries,
entryValuesKey = values
entryValuesKey = values,
),
ListPreference(
key = "revanced_alt_thumbnail_library",
entriesKey = entries,
entryValuesKey = values
entryValuesKey = values,
),
ListPreference(
key = "revanced_alt_thumbnail_player",
entriesKey = entries,
entryValuesKey = values
entryValuesKey = values,
),
ListPreference(
key = "revanced_alt_thumbnail_search",
entriesKey = entries,
entryValuesKey = values
entryValuesKey = values,
),
NonInteractivePreference(
"revanced_alt_thumbnail_dearrow_about",

View File

@@ -13,9 +13,10 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/BypassImageRegionRestrictionsPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Bypass image region restrictions` by creatingBytecodePatch(
description = "Adds an option to use a different host for user avatar and channel images " +
"and can fix missing images that are blocked in some countries.",
"and can fix missing images that are blocked in some countries.",
) {
dependsOn(
sharedExtensionPatch,
@@ -30,7 +31,7 @@ val `Bypass image region restrictions` by creatingBytecodePatch(
"20.14.43",
"20.21.37",
"20.31.40",
)
),
)
apply {

View File

@@ -26,6 +26,7 @@ internal var prefBackgroundAndOfflineCategoryId = -1L
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/BackgroundPlaybackPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Remove background playback restrictions` by creatingBytecodePatch(
description = "Removes restrictions on background playback, including playing kids videos in the background.",
) {

View File

@@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/spoof/SpoofDeviceDimensionsPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Spoof device dimensions` by creatingBytecodePatch(
description = "Adds an option to spoof the device dimensions which can unlock higher video qualities.",
) {

View File

@@ -19,13 +19,13 @@ internal val fixContentProviderPatch = bytecodePatch {
)
apply {
unstableContentProviderMethod.let {
val insertIndex = it.instructionMatches.first().index
unstableContentProviderMethodMatch.let {
val insertIndex = it.indices.first()
it.method.apply {
val register = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
it.method.addInstruction(
addInstruction(
insertIndex,
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->removeNullMapEntries(Ljava/util/Map;)V",
)

View File

@@ -1,25 +1,15 @@
package app.revanced.patches.youtube.misc.fix.contentprovider
import app.revanced.patcher.accessFlags
import app.revanced.patcher.addString
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
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
internal val BytecodePatchContext.unstableContentProviderMethod by gettingFirstMethodDeclaratively {
internal val unstableContentProviderMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("Landroid/content/ContentResolver;", "[Ljava/lang/String;")
instructions(
// Early targets use HashMap and later targets use ConcurrentMap.
methodCall(
name = "putAll",
parameters = listOf("Ljava/util/Map;"),
),
method { name == "putAll" && parameterTypes.count() == 1 && parameterTypes.first() == "Ljava/util/Map;" },
"ContentProvider query returned null cursor"(),
)
}

View File

@@ -14,6 +14,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/BypassURLRedirectsPatch;"
@Suppress("unused", "ObjectPropertyName")
val `Bypass URL redirects` by creatingBytecodePatch(
description = "Adds an option to bypass URL redirects and open the original URL directly.",
) {
@@ -41,22 +42,20 @@ val `Bypass URL redirects` by creatingBytecodePatch(
arrayOf(
if (is_20_37_or_greater) {
(abUriParserMethod to 2)
(abUriParserMethodMatch to 2)
} else {
(abUriParserLegacyMethod to 2)
(abUriParserLegacyMethodMatch to 2)
},
httpUriParserMethod to 0,
).forEach { (fingerprint, index) ->
fingerprint.method.apply {
val insertIndex = fingerprint.instructionMatches[index].index
val uriStringRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
httpUriParserMethodMatch to 0,
).forEach { (match, index) ->
val insertIndex = match.indices[index]
val uriStringRegister = match.method.getInstruction<FiveRegisterInstruction>(insertIndex).registerC
replaceInstruction(
insertIndex,
"invoke-static { v$uriStringRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
"parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;",
)
}
match.method.replaceInstruction(
insertIndex,
"invoke-static { v$uriStringRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
"parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;",
)
}
}
}

View File

@@ -3,18 +3,18 @@ package app.revanced.patches.youtube.misc.links
import app.revanced.patcher.StringComparisonType
import app.revanced.patcher.accessFlags
import app.revanced.patcher.addString
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.methodCall
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
/**
* 20.36 and lower.
*/
internal val BytecodePatchContext.abUriParserLegacyMethod by gettingFirstMethodDeclaratively {
internal val abUriParserLegacyMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Ljava/lang/Object;")
parameterTypes("Ljava/lang/Object;")
@@ -28,7 +28,7 @@ internal val BytecodePatchContext.abUriParserLegacyMethod by gettingFirstMethodD
/**
* 20.37+
*/
internal val BytecodePatchContext.abUriParserMethod by gettingFirstMethodDeclaratively {
internal val abUriParserMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Ljava/lang/Object;")
parameterTypes("Ljava/lang/Object;")
@@ -42,7 +42,7 @@ internal val BytecodePatchContext.abUriParserMethod by gettingFirstMethodDeclara
)
}
internal val BytecodePatchContext.httpUriParserMethod by gettingFirstMethodDeclaratively {
internal val httpUriParserMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returnType("Landroid/net/Uri;")
parameterTypes("Ljava/lang/String;")

View File

@@ -10,6 +10,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.StringReference
@Suppress("unused", "ObjectPropertyName")
val `Open links externally` by creatingBytecodePatch(
description = "Adds an option to always open links in your browser instead of the in-app browser.",
) {
@@ -44,7 +45,7 @@ val `Open links externally` by creatingBytecodePatch(
"20.14.43",
"20.21.37",
"20.31.40",
)
),
)
apply {

View File

@@ -1,16 +1,7 @@
package app.revanced.patches.youtube.misc.litho.filter
import app.revanced.patcher.accessFlags
import app.revanced.patcher.addString
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.opcodes
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import app.revanced.util.containsLiteralInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -23,26 +14,27 @@ internal val BytecodePatchContext.componentCreateMethod by gettingFirstMethodDec
}
internal val BytecodePatchContext.lithoFilterMethod by gettingFirstMethodDeclaratively {
definingClass { endsWith("/LithoFilterPatch;") }
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
custom { _, classDef ->
classDef.endsWith("/LithoFilterPatch;")
}
}
internal val BytecodePatchContext.protobufBufferReferenceMethod by gettingFirstMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("[B")
var methodDefiningClass = ""
custom {
methodDefiningClass = definingClass
true
}
instructions(
fieldAccess(
opcode = Opcode.IGET_OBJECT,
definingClass = "this",
type = "Lcom/google/android/libraries/elements/adl/UpbMessage;",
),
methodCall(
definingClass = "Lcom/google/android/libraries/elements/adl/UpbMessage;",
name = "jniDecode",
allOf(
Opcode.IGET_OBJECT(),
field { definingClass == methodDefiningClass && type == "Lcom/google/android/libraries/elements/adl/UpbMessage;" },
),
method { definingClass == "Lcom/google/android/libraries/elements/adl/UpbMessage;" && name == "jniDecode" },
)
}
@@ -61,20 +53,16 @@ internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by getting
internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDeclaratively {
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
parameterTypes()
instructions(
"EmptyComponent"(),
)
custom { _, classDef ->
classDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1
}
instructions("EmptyComponent"())
custom { immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 }
}
internal val BytecodePatchContext.lithoThreadExecutorMethod by gettingFirstMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameterTypes("I", "I", "I")
custom { method, classDef ->
classDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" &&
method.containsLiteralInstruction(1L) // 1L = default thread timeout.
custom {
immutableClassDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" &&
containsLiteralInstruction(1L) // 1L = default thread timeout.
}
}

View File

@@ -15,6 +15,7 @@ import com.android.tools.smali.dexlib2.Opcode
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/LoopVideoPatch;"
@Suppress("ObjectPropertyName")
val `Loop video` by creatingBytecodePatch(
description = "Adds an option to loop videos and display loop video button in the video player.",
) {

View File

@@ -122,7 +122,7 @@ internal fun getSeekbarOnDrawMethodMatch() = firstMethodComposite {
)
}
internal val BytecodePatchContext.subtitleButtonControllerMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.subtitleButtonControllerMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("Lcom/google/android/libraries/youtube/player/subtitles/model/SubtitleTrack;")