revanced-patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/patch/SettingsPatch.kt

132 lines
6.2 KiB
Kotlin
Raw Normal View History

2022-07-31 12:15:26 +02:00
package app.revanced.patches.youtube.misc.settings.bytecode.patch
import app.revanced.extensions.toErrorResult
2022-07-31 12:15:26 +02:00
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
2022-07-31 12:15:26 +02:00
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
2022-07-31 12:15:26 +02:00
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
2022-08-03 03:53:35 +02:00
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.util.AbstractPreferenceScreen
2022-07-31 12:15:26 +02:00
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.LicenseActivityFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.SetThemeFingerprint
2022-07-31 12:15:26 +02:00
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.util.MethodUtil
2022-07-31 12:15:26 +02:00
@DependsOn([IntegrationsPatch::class, SettingsResourcePatch::class, ])
2022-07-31 12:15:26 +02:00
@Name("settings")
@Description("Adds settings for ReVanced to YouTube.")
@Version("0.0.1")
class SettingsPatch : BytecodePatch(
listOf(LicenseActivityFingerprint, SetThemeFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
// TODO: Remove this when it is only required at one place.
fun getSetThemeInstructionString(
registers: String = "v0",
classDescriptor: String = THEME_HELPER_DESCRIPTOR,
methodName: String = SET_THEME_METHOD_NAME,
parameters: String = "Ljava/lang/Object;"
) = "invoke-static { $registers }, $classDescriptor->$methodName($parameters)V"
SetThemeFingerprint.result?.mutableMethod?.let { setThemeMethod ->
setThemeMethod.implementation!!.instructions.mapIndexedNotNull { i, instruction ->
if (instruction.opcode == Opcode.RETURN_OBJECT) i else null
}
.asReversed() // Prevent index shifting.
.forEach { returnIndex ->
// The following strategy is to replace the return instruction with the setTheme instruction,
// then add a return instruction after the setTheme instruction.
// This is done because the return instruction is a target of another instruction.
setThemeMethod.apply {
// This register is returned by the setTheme method.
val register = instruction<OneRegisterInstruction>(returnIndex).registerA
val setThemeInstruction = getSetThemeInstructionString("v$register")
replaceInstruction(returnIndex, setThemeInstruction)
addInstruction(returnIndex + 1, "return-object v0")
}
}
} ?: return SetThemeFingerprint.toErrorResult()
// set the theme based on the preference of the device
LicenseActivityFingerprint.result!!.apply licenseActivity@{
mutableMethod.apply {
fun buildSettingsActivityInvokeString(
registers: String = "p0",
classDescriptor: String = SETTINGS_ACTIVITY_DESCRIPTOR,
methodName: String = "initializeSettings",
parameters: String = this@licenseActivity.mutableClass.type
) = getSetThemeInstructionString(registers, classDescriptor, methodName, parameters)
// initialize the settings
addInstructions(
1, """
${buildSettingsActivityInvokeString()}
return-void
"""
)
2022-07-31 12:15:26 +02:00
// set the current theme
addInstruction(0, buildSettingsActivityInvokeString(methodName = "setTheme"))
}
// remove method overrides
mutableClass.apply {
methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) }
}
}
2022-07-31 12:15:26 +02:00
return PatchResultSuccess()
}
internal companion object {
private const val INTEGRATIONS_PACKAGE = "app/revanced/integrations"
private const val SETTINGS_ACTIVITY_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/settingsmenu/ReVancedSettingActivity;"
private const val THEME_HELPER_DESCRIPTOR = "L$INTEGRATIONS_PACKAGE/utils/ThemeHelper;"
private const val SET_THEME_METHOD_NAME = "setTheme"
fun addString(identifier: String, value: String, formatted: Boolean = true) =
SettingsResourcePatch.addString(identifier, value, formatted)
fun addPreferenceScreen(preferenceScreen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) =
SettingsResourcePatch.addPreferenceScreen(preferenceScreen)
fun addPreference(preference: Preference) = SettingsResourcePatch.addPreference(preference)
fun renameIntentsTargetPackage(newPackage: String) {
SettingsResourcePatch.overrideIntentsTargetPackage = newPackage
}
}
/**
* Preference screens patches should add their settings to.
*/
internal object PreferenceScreen : AbstractPreferenceScreen() {
val ADS = Screen("ads", "Ads", "Ad related settings")
val INTERACTIONS = Screen("interactions", "Interaction", "Settings related to interactions")
val LAYOUT = Screen("layout", "Layout", "Settings related to the layout")
val VIDEO = Screen("video", "Video", "Settings related to the video player")
val MISC = Screen("misc", "Misc", "Miscellaneous patches")
override fun commit(screen: app.revanced.patches.shared.settings.preference.impl.PreferenceScreen) {
addPreferenceScreen(screen)
}
}
override fun close() = PreferenceScreen.close()
}