fix(youtube/theme): theme litho ui components & use correct theme for settings (#791)

This commit is contained in:
OxrxL 2022-10-25 08:36:59 +02:00 committed by GitHub
parent 94a8e4b022
commit 91c03c5624
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 95 deletions

View File

@ -8,10 +8,10 @@ import app.revanced.patches.youtube.layout.theme.annotations.ThemeCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("comment-actionbar-fingerprint")
@Name("litho-ui-fingerprint")
@ThemeCompatibility
@Version("0.0.1")
object CommentsFilterBarFingerprint : MethodFingerprint(
object LithoThemeFingerprint : MethodFingerprint(
"V", AccessFlags.PROTECTED or AccessFlags.FINAL, listOf("L"), listOf(
Opcode.APUT,
Opcode.NEW_INSTANCE,

View File

@ -11,36 +11,27 @@ import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.layout.theme.annotations.ThemeCompatibility
import app.revanced.patches.youtube.layout.theme.fingerprints.CommentsFilterBarFingerprint
import app.revanced.patches.youtube.layout.theme.fingerprints.LithoThemeFingerprint
@Name("comment-filter-bar-theme")
@Description("Applies custom theming to comments filter action bar.")
@Name("litho-components-theme")
@Description("Applies a custom theme to litho components.")
@ThemeCompatibility
@Version("0.0.1")
class CommentsFilterBarPatch : BytecodePatch(
class LithoThemePatch : BytecodePatch(
listOf(
CommentsFilterBarFingerprint
LithoThemeFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = CommentsFilterBarFingerprint.result!!
val result = LithoThemeFingerprint.result!!
val method = result.mutableMethod
val patchIndex = result.scanResult.patternScanResult!!.endIndex - 1
method.addInstructions(
patchIndex, """
invoke-static {}, Lapp/revanced/integrations/utils/ThemeHelper;->isDarkTheme()Z
move-result v2
if-nez v2, :comments_filter_white
const v1, -0x1
if-ne v1, p1, :comments_filter_white
const/4 p1, 0x0
:comments_filter_white
if-eqz v2, :comments_filter_dark
const v1, -0xdededf
if-ne v1, p1, :comments_filter_dark
const/4 p1, 0x0
""", listOf(ExternalLabel("comments_filter_dark", method.instruction(patchIndex)))
invoke-static {p1}, Lapp/revanced/integrations/patches/LithoThemePatch;->applyLithoTheme(I)I
move-result p1
"""
)
return PatchResultSuccess()
}

View File

@ -14,7 +14,7 @@ import app.revanced.util.resources.ResourceUtils.copyResources
import org.w3c.dom.Element
@Patch
@DependsOn([CommentsFilterBarPatch::class, FixLocaleConfigErrorPatch::class])
@DependsOn([LithoThemePatch::class, FixLocaleConfigErrorPatch::class])
@Name("theme")
@Description("Applies a custom theme.")
@ThemeCompatibility

View File

@ -1,17 +0,0 @@
package app.revanced.patches.youtube.misc.settings.bytecode.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
// TODO: This is more of a class fingerprint than a method fingerprint.
// Convert to a class fingerprint whenever possible.
@Name("revanced-settings-activity-fingerprint")
@SettingsCompatibility
@Version("0.0.1")
object ReVancedSettingsActivityFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("ReVancedSettingActivity;") && methodDef.name == "initializeSettings"
}
)

View File

@ -0,0 +1,39 @@
package app.revanced.patches.youtube.misc.settings.bytecode.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("theme-setter-app-fingerprint")
@SettingsCompatibility
@Version("0.0.1")
object ThemeSetterAppFingerprint : MethodFingerprint(
"L", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L", "L"), listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.SGET_OBJECT,
Opcode.IF_NE,
Opcode.CONST,
Opcode.GOTO,
Opcode.CONST,
Opcode.INVOKE_DIRECT,
Opcode.RETURN_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.SGET_OBJECT,
Opcode.IF_NE,
Opcode.CONST,
)
)

View File

@ -5,18 +5,19 @@ import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
@Name("theme-setter-fingerprint")
@Name("theme-setter-system-fingerprint")
@SettingsCompatibility
@Version("0.0.1")
object ThemeSetterFingerprint : MethodFingerprint(
object ThemeSetterSystemFingerprint : MethodFingerprint(
"L",
opcodes = listOf(Opcode.RETURN_OBJECT),
customFingerprint = { methodDef ->
methodDef.implementation?.instructions?.any {
it.opcode.ordinal == Opcode.CONST.ordinal && (it as WideLiteralInstruction).wideLiteral == SettingsPatch.appearanceStringId
it.opcode.ordinal == Opcode.CONST.ordinal && (it as WideLiteralInstruction).wideLiteral == SettingsResourcePatch.appearanceStringId
} == true
}
)

View File

@ -11,15 +11,12 @@ import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.LicenseActivityFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ReVancedSettingsActivityFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterAppFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterSystemFingerprint
import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference
import app.revanced.patches.youtube.misc.settings.framework.components.impl.ArrayResource
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
import app.revanced.patches.youtube.misc.settings.framework.components.impl.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
@ -39,64 +36,92 @@ import java.io.Closeable
@SettingsCompatibility
@Version("0.0.1")
class SettingsPatch : BytecodePatch(
listOf(LicenseActivityFingerprint, ReVancedSettingsActivityFingerprint, ThemeSetterFingerprint)
listOf(LicenseActivityFingerprint, ThemeSetterSystemFingerprint, ThemeSetterAppFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
val licenseActivityResult = LicenseActivityFingerprint.result!!
val settingsResult = ReVancedSettingsActivityFingerprint.result!!
val themeSetterResult = ThemeSetterFingerprint.result!!
fun buildInvokeInstructionsString(
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"
val licenseActivityClass = licenseActivityResult.mutableClass
val settingsClass = settingsResult.mutableClass
// apply the current theme of the settings page
with(ThemeSetterSystemFingerprint.result!!) {
with(mutableMethod) {
val call = buildInvokeInstructionsString()
val onCreate = licenseActivityResult.mutableMethod
val setThemeMethodName = "setTheme"
val initializeSettings = settingsResult.mutableMethod
addInstruction(
scanResult.patternScanResult!!.startIndex,
call
)
val setThemeInstruction =
"invoke-static {v0}, Lapp/revanced/integrations/utils/ThemeHelper;->setTheme(Ljava/lang/Object;)V".toInstruction(
themeSetterResult.mutableMethod
)
// add instructions to set the theme of the settings activity
themeSetterResult.mutableMethod.implementation!!.let {
it.addInstruction(
themeSetterResult.scanResult.patternScanResult!!.startIndex,
setThemeInstruction
)
it.addInstruction(
it.instructions.size - 1, // add before return
setThemeInstruction
)
addInstruction(
mutableMethod.implementation!!.instructions.size - 1,
call
)
}
}
// add the setTheme call to the onCreate method to not affect the offsets
onCreate.addInstructions(
1,
"""
invoke-static { p0 }, ${settingsClass.type}->${initializeSettings.name}(${licenseActivityClass.type})V
return-void
"""
)
// set the theme based on the preference of the app
with(ThemeSetterAppFingerprint.result!!) {
with(mutableMethod) {
fun buildInstructionsString(theme: Int) = """
const/4 v0, 0x$theme
${buildInvokeInstructionsString(parameters = "I")}
"""
// add the initializeSettings call to the onCreate method
onCreate.addInstruction(
0,
"invoke-static { p0 }, ${settingsClass.type}->$setThemeMethodName(${licenseActivityClass.type})V"
)
addInstructions(
scanResult.patternScanResult!!.endIndex + 1,
buildInstructionsString(1)
)
// get rid of, now, useless overridden methods
licenseActivityResult.mutableClass.methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) }
addInstructions(
mutableMethod.implementation!!.instructions.size - 2,
buildInstructionsString(0)
)
}
}
// set the theme based on the preference of the device
with(LicenseActivityFingerprint.result!!) licenseActivity@{
with(mutableMethod) {
fun buildSettingsActivityInvokeString(
registers: String = "p0",
classDescriptor: String = SETTINGS_ACTIVITY_DESCRIPTOR,
methodName: String = "initializeSettings",
parameters: String = this@licenseActivity.mutableClass.type
) = buildInvokeInstructionsString(registers, classDescriptor, methodName, parameters)
// initialize the settings
addInstructions(
1,
"""
${buildSettingsActivityInvokeString()}
return-void
"""
)
// set the current theme
addInstruction(0, buildSettingsActivityInvokeString(methodName = "setTheme"))
}
// remove method overrides
with(mutableClass) {
methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) }
}
}
return PatchResultSuccess()
}
internal companion object {
// TODO: hide this somehow
var appearanceStringId: Long = ResourceMappingResourcePatch.resourceMappings.find {
it.type == "string" && it.name == "app_theme_appearance_dark"
}!!.id
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)
@ -107,9 +132,6 @@ class SettingsPatch : BytecodePatch(
fun addPreference(preference: Preference) =
SettingsResourcePatch.addPreference(preference)
fun addArray(arrayResource: ArrayResource) =
SettingsResourcePatch.addArray(arrayResource)
fun renameIntentsTargetPackage(newPackage: String) {
SettingsResourcePatch.overrideIntentsTargetPackage = newPackage
}
@ -152,4 +174,4 @@ class SettingsPatch : BytecodePatch(
override fun close() = PreferenceScreen.values().forEach(PreferenceScreen::close)
}
}

View File

@ -24,8 +24,14 @@ import org.w3c.dom.Node
@DependsOn([FixLocaleConfigErrorPatch::class, ResourceMappingResourcePatch::class])
@Version("0.0.1")
class SettingsResourcePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
/*
* used by a fingerprint of SettingsPatch
*/
appearanceStringId = ResourceMappingResourcePatch.resourceMappings.find {
it.type == "string" && it.name == "app_theme_appearance_dark"
}!!.id
/*
* create missing directory for the resources
*/
@ -84,6 +90,12 @@ class SettingsResourcePatch : ResourcePatch {
internal companion object {
// Used by a fingerprint of SettingsPatch
// this field is located in the SettingsResourcePatch
// because if it were to be defined in the SettingsPatch companion object,
// the companion object could be initialized before ResourceMappingResourcePatch has executed.
internal var appearanceStringId: Long = -1
// if this is not null, all intents will be renamed to this
var overrideIntentsTargetPackage: String? = null