Skip to content

Commit

Permalink
fix(youtube/theme): apply custom seekbar color to video thumbnails (R…
Browse files Browse the repository at this point in the history
  • Loading branch information
LisoUseInAIKyrios authored May 11, 2023
1 parent 1b76da8 commit d497027
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ import app.revanced.patches.shared.fingerprints.SeekbarOnDrawFingerprint
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.youtube.layout.hide.seekbar.annotations.HideSeekbarCompatibility
import app.revanced.patches.youtube.layout.seekbar.bytecode.patch.SeekbarColorBytecodePatch
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch

@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])
@DependsOn([
IntegrationsPatch::class,
SettingsPatch::class,
SeekbarColorBytecodePatch::class // Used to hide the seekbar in the feed and watch history
])
@Name("hide-seekbar")
@Description("Hides the seekbar.")
@HideSeekbarCompatibility
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package app.revanced.patches.youtube.layout.seekbar.annotations

import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package

@Compatibility([Package("com.google.android.youtube")])
@Target(AnnotationTarget.CLASS)
internal annotation class SeekbarColorCompatibility
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package app.revanced.patches.youtube.layout.seekbar.bytecode.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.layout.seekbar.resource.SeekbarColorResourcePatch
import app.revanced.util.patch.indexOfFirstConstantInstruction
import org.jf.dexlib2.AccessFlags

object CreateDarkThemeSeekbarFingerprint : MethodFingerprint(
access = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { method ->
method.indexOfFirstConstantInstruction(SeekbarColorResourcePatch.inlineTimeBarColorizedBarPlayedColorDarkId) != -1
&& method.indexOfFirstConstantInstruction(SeekbarColorResourcePatch.inlineTimeBarPlayedNotHighlightedColorId) != -1
}
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.theme.bytecode.fingerprints
package app.revanced.patches.youtube.layout.seekbar.bytecode.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package app.revanced.patches.youtube.layout.seekbar.bytecode.patch

import app.revanced.extensions.toErrorResult
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.toMethodWalker
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.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.layout.seekbar.annotations.SeekbarColorCompatibility
import app.revanced.patches.youtube.layout.seekbar.bytecode.fingerprints.CreateDarkThemeSeekbarFingerprint
import app.revanced.patches.youtube.layout.seekbar.bytecode.fingerprints.SetSeekbarClickedColorFingerprint
import app.revanced.patches.youtube.layout.theme.bytecode.patch.LithoColorHookPatch
import app.revanced.patches.youtube.layout.theme.bytecode.patch.LithoColorHookPatch.Companion.lithoColorOverrideHook
import app.revanced.patches.youtube.layout.seekbar.resource.SeekbarColorResourcePatch
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.util.patch.indexOfFirstConstantInstruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction

@Description("Hide or set a custom seekbar color")
@DependsOn([IntegrationsPatch::class, LithoColorHookPatch::class, SeekbarColorResourcePatch::class])
@SeekbarColorCompatibility
@Version("0.0.1")
class SeekbarColorBytecodePatch : BytecodePatch(
listOf(CreateDarkThemeSeekbarFingerprint, SetSeekbarClickedColorFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
CreateDarkThemeSeekbarFingerprint.result?.mutableMethod?.apply {
var registerIndex = indexOfFirstConstantInstruction(SeekbarColorResourcePatch.inlineTimeBarColorizedBarPlayedColorDarkId) + 2
var colorRegister = (instruction(registerIndex) as OneRegisterInstruction).registerA
addInstructions(
registerIndex + 1,
"""
invoke-static { v$colorRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarColorValue(I)I
move-result v$colorRegister
"""
)

registerIndex = indexOfFirstConstantInstruction(SeekbarColorResourcePatch.inlineTimeBarPlayedNotHighlightedColorId) + 2
colorRegister = (instruction(registerIndex) as OneRegisterInstruction).registerA
addInstructions(
registerIndex + 1,
"""
invoke-static { v$colorRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarColorValue(I)I
move-result v$colorRegister
"""
)
} ?: return CreateDarkThemeSeekbarFingerprint.toErrorResult()

SetSeekbarClickedColorFingerprint.result?.let { result ->
result.mutableMethod.let {
val setColorMethodIndex = result.scanResult.patternScanResult!!.startIndex + 1
val method = context
.toMethodWalker(it)
.nextMethod(setColorMethodIndex, true)
.getMethod() as MutableMethod

method.apply {
val colorRegister = (method.instruction(0) as TwoRegisterInstruction).registerA
addInstructions(
0,
"""
invoke-static { v$colorRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarColorOverride(I)I
move-result v$colorRegister
"""
)
}
}
} ?: return SetSeekbarClickedColorFingerprint.toErrorResult()

lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getSeekbarColorOverride")

return PatchResultSuccess()
}

private companion object {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/theme/SeekbarColorPatch;"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package app.revanced.patches.youtube.layout.seekbar.resource

import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import org.w3c.dom.Element

@DependsOn([SettingsPatch::class, ResourceMappingPatch::class])
class SeekbarColorResourcePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
// Edit theme colors via bytecode.
// For that the resource id is used in a bytecode patch to change the color.

val seekbarErrorMessage = "Could not find seekbar resource"
inlineTimeBarColorizedBarPlayedColorDarkId = ResourceMappingPatch.resourceMappings
.find { it.name == "inline_time_bar_colorized_bar_played_color_dark" }?.id
?: return PatchResultError(seekbarErrorMessage)
inlineTimeBarPlayedNotHighlightedColorId = ResourceMappingPatch.resourceMappings
.find { it.name == "inline_time_bar_played_not_highlighted_color" }?.id
?: return PatchResultError(seekbarErrorMessage)

// Edit the resume playback drawable and replace the progress bar with a custom drawable
context.xmlEditor["res/drawable/resume_playback_progressbar_drawable.xml"].use { editor ->
val layerList = editor.file.getElementsByTagName("layer-list").item(0) as Element
val progressNode = layerList.getElementsByTagName("item").item(1) as Element
if (!progressNode.getAttributeNode("android:id").value.endsWith("progress")) {
return PatchResultError("Could not find progress bar")
}
val scaleNode = progressNode.getElementsByTagName("scale").item(0) as Element
val shapeNode = scaleNode.getElementsByTagName("shape").item(0) as Element
val replacementNode = editor.file.createElement(
"app.revanced.integrations.patches.theme.ProgressBarDrawable")
scaleNode.replaceChild(replacementNode, shapeNode)
}

return PatchResultSuccess()
}

companion object {
internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L
internal var inlineTimeBarPlayedNotHighlightedColorId = -1L
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.theme.fingerprints
package app.revanced.patches.youtube.layout.theme.bytecode.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.layout.theme.annotations.ThemeCompatibility
import app.revanced.patches.youtube.layout.theme.bytecode.fingerprints.LithoThemeFingerprint

@Name("litho-color-hook")
@Description("Adds a hook to set color of Litho components.")
@ThemeCompatibility
@Version("0.0.1")
class LithoColorHookPatch : BytecodePatch(listOf(LithoThemeFingerprint)) {
override fun execute(context: BytecodeContext): PatchResult {
LithoThemeFingerprint.result?.let {
insertionIndex = it.scanResult.patternScanResult!!.endIndex - 1
colorRegister = "p1"
insertionMethod = it.mutableMethod
} ?: return LithoThemeFingerprint.toErrorResult()

return PatchResultSuccess()
}
companion object {
private var insertionIndex : Int = -1
private lateinit var colorRegister : String
private lateinit var insertionMethod : MutableMethod

internal fun lithoColorOverrideHook(targetMethodClass: String, targetMethodName: String) {
insertionMethod.addInstructions(
insertionIndex,
"""
invoke-static {$colorRegister}, $targetMethodClass->$targetMethodName(I)I
move-result $colorRegister
"""
)
insertionIndex += 2
}
}

}
Original file line number Diff line number Diff line change
@@ -1,76 +1,34 @@
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.data.toMethodWalker
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
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.bytecode.fingerprints.SetSeekbarClickedColorFingerprint
import app.revanced.patches.youtube.layout.seekbar.bytecode.patch.SeekbarColorBytecodePatch
import app.revanced.patches.youtube.layout.seekbar.bytecode.fingerprints.CreateDarkThemeSeekbarFingerprint
import app.revanced.patches.youtube.layout.seekbar.bytecode.fingerprints.SetSeekbarClickedColorFingerprint
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
@DependsOn([LithoColorHookPatch::class, SeekbarColorBytecodePatch::class, ThemeResourcePatch::class])
@Version("0.0.1")
class ThemeBytecodePatch : BytecodePatch(
listOf(CreateDarkThemeSeekbarFingerprint, SetSeekbarClickedColorFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
CreateDarkThemeSeekbarFingerprint.result?.let {
val putColorValueIndex = it.method.indexOfInstructionWithSeekbarId!! + 3

it.mutableMethod.apply {
val overrideRegister = instruction<TwoRegisterInstruction>(putColorValueIndex).registerA

addInstructions(
putColorValueIndex,
"""
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarColorValue()I
move-result v$overrideRegister
"""
)
}
} ?: return CreateDarkThemeSeekbarFingerprint.toErrorResult()

SetSeekbarClickedColorFingerprint.result?.let { result ->
result.mutableMethod.let {
val setColorMethodIndex = result.scanResult.patternScanResult!!.startIndex + 1
val method = context
.toMethodWalker(it)
.nextMethod(setColorMethodIndex, true)
.getMethod() as MutableMethod
override fun execute(context: BytecodeContext): PatchResult {
LithoColorHookPatch.lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getValue")

method.apply {
val colorRegister = method.instruction<TwoRegisterInstruction>(0).registerA
addInstructions(
0,
"""
invoke-static { v$colorRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarClickedColorValue(I)I
move-result v$colorRegister
"""
)
}
}
} ?: return SetSeekbarClickedColorFingerprint.toErrorResult()
return PatchResultSuccess()
}

companion object : OptionsContainer() {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/theme/ThemePatch;"
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/theme/ThemeLithoComponentsPatch;"

var darkThemeBackgroundColor: String? by option(
PatchOption.StringOption(
Expand Down

This file was deleted.

Loading

0 comments on commit d497027

Please sign in to comment.