feat(youtube/theme): change seekbar color via preference

This commit is contained in:
oSumAtrIX 2023-05-02 05:30:14 +02:00
parent feceb1c056
commit 9b465d9588
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
5 changed files with 153 additions and 63 deletions

View File

@ -0,0 +1,25 @@
package app.revanced.patches.youtube.layout.theme.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.layout.theme.bytecode.fingerprints.CreateDarkThemeSeekbarFingerprint.indexOfInstructionWithSeekbarId
import app.revanced.patches.youtube.layout.theme.resource.ThemeResourcePatch
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
object CreateDarkThemeSeekbarFingerprint : MethodFingerprint(
access = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { method -> method.indexOfInstructionWithSeekbarId != -1 },
) {
/**
* The index of the instruction that loads the resource id of the seekbar.
*/
internal val Method.indexOfInstructionWithSeekbarId
get() = implementation?.let {
it.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.CONST && (instruction as WideLiteralInstruction).wideLiteral == ThemeResourcePatch.inlineTimeBarColorizedBarPlayedColorDarkId
}
}
}

View File

@ -0,0 +1,51 @@
package app.revanced.patches.youtube.layout.theme.bytecode.patch
import app.revanced.extensions.toErrorResult
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.BytecodePatch
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.patches.youtube.layout.theme.annotations.ThemeCompatibility
import app.revanced.patches.youtube.layout.theme.bytecode.fingerprints.CreateDarkThemeSeekbarFingerprint
import app.revanced.patches.youtube.layout.theme.bytecode.fingerprints.CreateDarkThemeSeekbarFingerprint.indexOfInstructionWithSeekbarId
import app.revanced.patches.youtube.layout.theme.resource.ThemeResourcePatch
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch
@Name("theme")
@Description("Applies a custom theme.")
@DependsOn([ThemeLithoComponentsPatch::class, ThemeResourcePatch::class, IntegrationsPatch::class])
@ThemeCompatibility
@Version("0.0.1")
class ThemeBytecodePatch : BytecodePatch(listOf(CreateDarkThemeSeekbarFingerprint)) {
override fun execute(context: BytecodeContext): PatchResult {
CreateDarkThemeSeekbarFingerprint.result?.let {
val putColorValueIndex = it.method.indexOfInstructionWithSeekbarId!! + 3
it.mutableMethod.apply {
val overrideRegister = (instruction(putColorValueIndex) as TwoRegisterInstruction).registerA
addInstructions(
putColorValueIndex,
"""
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarColorValue()I
move-result v$overrideRegister
"""
)
}
} ?: return CreateDarkThemeSeekbarFingerprint.toErrorResult()
return PatchResultSuccess()
}
private companion object {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/theme/ThemePatch;"
}
}

View File

@ -0,0 +1,40 @@
package app.revanced.patches.youtube.layout.theme.bytecode.patch
import app.revanced.extensions.toErrorResult
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patches.youtube.layout.theme.annotations.ThemeCompatibility
import app.revanced.patches.youtube.layout.theme.fingerprints.LithoThemeFingerprint
@Name("theme-litho-components")
@Description("Applies a custom theme to Litho components.")
@ThemeCompatibility
@Version("0.0.1")
class ThemeLithoComponentsPatch : BytecodePatch(listOf(LithoThemeFingerprint)) {
override fun execute(context: BytecodeContext): PatchResult {
LithoThemeFingerprint.result?.let {
it.mutableMethod.apply {
val patchIndex = it.scanResult.patternScanResult!!.endIndex - 1
addInstructions(
patchIndex,
"""
invoke-static {p1}, $INTEGRATIONS_CLASS_DESCRIPTOR->getValue(I)I
move-result p1
"""
)
}
} ?: return LithoThemeFingerprint.toErrorResult()
return PatchResultSuccess()
}
private companion object {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/theme/ThemeLithoComponentsPatch;"
}
}

View File

@ -1,36 +0,0 @@
package app.revanced.patches.youtube.layout.theme.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patches.youtube.layout.theme.annotations.ThemeCompatibility
import app.revanced.patches.youtube.layout.theme.fingerprints.LithoThemeFingerprint
@Name("litho-components-theme")
@Description("Applies a custom theme to litho components.")
@ThemeCompatibility
@Version("0.0.1")
class LithoThemePatch : BytecodePatch(
listOf(
LithoThemeFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = LithoThemeFingerprint.result!!
val method = result.mutableMethod
val patchIndex = result.scanResult.patternScanResult!!.endIndex - 1
method.addInstructions(
patchIndex, """
invoke-static {p1}, Lapp/revanced/integrations/patches/LithoThemePatch;->applyLithoTheme(I)I
move-result p1
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,29 +1,45 @@
package app.revanced.patches.youtube.layout.theme.patch package app.revanced.patches.youtube.layout.theme.resource
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.* import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.layout.theme.annotations.ThemeCompatibility import app.revanced.patches.shared.settings.preference.impl.InputType
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.TextPreference
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.util.resources.ResourceUtils import app.revanced.util.resources.ResourceUtils
import app.revanced.util.resources.ResourceUtils.copyResources import app.revanced.util.resources.ResourceUtils.copyResources
import org.w3c.dom.Element import org.w3c.dom.Element
@Patch @DependsOn([SettingsPatch::class, ResourceMappingPatch::class])
@DependsOn([LithoThemePatch::class]) class ThemeResourcePatch : ResourcePatch {
@Name("theme")
@Description("Applies a custom theme.")
@ThemeCompatibility
@Version("0.0.1")
class ThemePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult { override fun execute(context: ResourceContext): PatchResult {
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
TextPreference(
"revanced_seekbar_color",
StringResource("revanced_seekbar_color_title", "Seekbar color"),
InputType.STRING,
"#ffff0000",
StringResource(
"revanced_seekbar_color_summary",
"The color of the seekbar for the dark theme."
)
),
)
// Edit theme colors via bytecode.
// For that the resource id is used in a bytecode patch to change the color.
inlineTimeBarColorizedBarPlayedColorDarkId = ResourceMappingPatch.resourceMappings
.find { it.name == "inline_time_bar_colorized_bar_played_color_dark" }?.id
?: return PatchResultError("Could not find seekbar resource")
val darkThemeBackgroundColor = darkThemeBackgroundColor!! val darkThemeBackgroundColor = darkThemeBackgroundColor!!
val lightThemeBackgroundColor = lightThemeBackgroundColor!! val lightThemeBackgroundColor = lightThemeBackgroundColor!!
val darkThemeSeekbarColor = darkThemeSeekbarColor!!
// Edit theme colors via resources.
context.xmlEditor["res/values/colors.xml"].use { editor -> context.xmlEditor["res/values/colors.xml"].use { editor ->
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
@ -31,18 +47,19 @@ class ThemePatch : ResourcePatch {
val node = resourcesNode.childNodes.item(i) as? Element ?: continue val node = resourcesNode.childNodes.item(i) as? Element ?: continue
node.textContent = when (node.getAttribute("name")) { node.textContent = when (node.getAttribute("name")) {
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98", "yt_black2", "yt_black3", "yt_black4", "yt_status_bar_background_dark", "material_grey_850" -> darkThemeBackgroundColor "yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98", "yt_black2", "yt_black3",
"yt_black4", "yt_status_bar_background_dark", "material_grey_850" -> darkThemeBackgroundColor
"yt_white1", "yt_white1_opacity95", "yt_white1_opacity98", "yt_white2", "yt_white3", "yt_white4", "yt_white1", "yt_white1_opacity95", "yt_white1_opacity98",
"yt_white2", "yt_white3", "yt_white4",
-> lightThemeBackgroundColor -> lightThemeBackgroundColor
"inline_time_bar_colorized_bar_played_color_dark" -> darkThemeSeekbarColor
else -> continue else -> continue
} }
} }
} }
// copies the resource file to change the splash screen color // Copy the resource file to change the splash screen color.
context.copyResources( context.copyResources(
"theme", ResourceUtils.ResourceGroup("values-night-v31", "styles.xml") "theme", ResourceUtils.ResourceGroup("values-night-v31", "styles.xml")
) )
@ -51,6 +68,8 @@ class ThemePatch : ResourcePatch {
} }
companion object : OptionsContainer() { companion object : OptionsContainer() {
internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L
var darkThemeBackgroundColor: String? by option( var darkThemeBackgroundColor: String? by option(
PatchOption.StringOption( PatchOption.StringOption(
key = "darkThemeBackgroundColor", key = "darkThemeBackgroundColor",
@ -68,14 +87,5 @@ class ThemePatch : ResourcePatch {
description = "The background color of the light theme. Can be a hex color or a resource reference.", description = "The background color of the light theme. Can be a hex color or a resource reference.",
) )
) )
var darkThemeSeekbarColor: String? by option(
PatchOption.StringOption(
key = "darkThemeSeekbarColor",
default = "#ffff0000",
title = "Dark theme seekbar color",
description = "The background color of the seekbar of the dark theme. Leave empty for default color.",
)
)
} }
} }