From 9f3adb80b95423e92cef4e569036fa1fe63dce98 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sun, 24 Nov 2024 03:06:32 +0400 Subject: [PATCH 1/7] feat(YouTube - Theme): Apply custom seekbar color to splash screen animation --- .../patches/theme/SeekbarColorPatch.java | 60 ++++++- .../youtube/layout/seekbar/Fingerprints.kt | 30 ++++ .../layout/seekbar/SeekbarColorPatch.kt | 149 +++++++++++++++++- .../shortsautoplay/ShortsAutoplayPatch.kt | 2 +- .../kotlin/app/revanced/util/ResourceUtils.kt | 1 - .../main/resources/seekbar/values/attrs.xml | 4 + 6 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 patches/src/main/resources/seekbar/values/attrs.xml diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index aea5c227c8..28cd660517 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -2,9 +2,12 @@ import static app.revanced.extension.shared.StringRef.str; +import android.content.res.Resources; import android.graphics.Color; +import android.graphics.drawable.AnimatedVectorDrawable; import java.util.Arrays; +import java.util.Locale; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; @@ -16,7 +19,7 @@ public final class SeekbarColorPatch { private static final boolean SEEKBAR_CUSTOM_COLOR_ENABLED = Settings.SEEKBAR_CUSTOM_COLOR.get(); /** - * Default color of the seekbar. + * Default color of the seekbar. Differs slightly from the custom color used in the settings. */ private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000; @@ -72,12 +75,65 @@ public static int getSeekbarColor() { return seekbarColor; } + /** + * Injection point + */ public static boolean playerSeekbarGradientEnabled(boolean original) { if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false; return original; } + /** + * Injection point + */ + public static boolean useLotteLaunchSplashScreen(boolean original) { + Logger.printDebug(() -> "useLotteLaunchSplashScreen original: " + original); + + if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false; + + return original; + } + + private static String get8BitStyleIdentifier(int color24Bit) { + // Convert to nearest 3-3-2 bit depth. + final int r3 = Math.round(Color.red(color24Bit) * 7 / 255f); + final int g3 = Math.round(Color.green(color24Bit) * 7 / 255f); + final int b2 = Math.round(Color.blue(color24Bit) * 3 / 255f); + + return String.format(Locale.US, "splash_seekbar_color_style_%d_%d_%d", r3, g3, b2); + } + + /** + * Injection point + */ + public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vectorDrawable) { + // Alternatively a ColorMatrixColorFilter can be used to change the color of the drawable + // without using any styles, but a color filter cannot selectively change the seekbar + // while keeping the red YT logo untouched. + // Even if the seekbar color xml value is changed to a completely different color (such as green), + // a color filter still cannot be selectively applied when the drawable has more than 1 color. + try { + String seekbarStyle = get8BitStyleIdentifier(seekbarColor); + Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle); + + final int styleIdentifierDefault = Utils.getResourceIdentifier( + seekbarStyle, + "style" + ); + if (styleIdentifierDefault == 0) { + throw new RuntimeException("Seekbar style not found: " + seekbarStyle); + } + + Resources.Theme theme = Utils.getContext().getResources().newTheme(); + theme.applyStyle(styleIdentifierDefault, true); + + vectorDrawable.applyTheme(theme); + } catch (Exception ex) { + Logger.printException(() -> "setSplashAnimationDrawableTheme failure", ex); + } + } + /** * Injection point. * @@ -189,4 +245,4 @@ private static int clamp(int value, int lower, int upper) { private static float clamp(float value, float lower, float upper) { return Math.max(lower, Math.min(value, upper)); } -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt index 67a6b017d5..6404a3ebbd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt @@ -48,3 +48,33 @@ internal val lithoLinearGradientFingerprint = fingerprint { returns("Landroid/graphics/LinearGradient;") parameters("F", "F", "F", "F", "[I", "[F") } + +internal const val launchScreenLayoutTypeLotteFeatureFlag = 268507948L + +internal val launchScreenLayoutTypeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters("Lcom/google/android/apps/youtube/app/watchwhile/MainActivity;", "L", "L", "L", "L", "L", "L", "L") + literal { launchScreenLayoutTypeLotteFeatureFlag } +} + +internal val launchScreenOptimizedFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters("L") + custom { method, _ -> + method.containsLiteralInstruction(268639016) + && method.containsLiteralInstruction(4) + } +} + +internal val launchScreenBuenosAiresFeatureFlagFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters("L") + custom { method, _ -> + method.containsLiteralInstruction(268639016) + && method.containsLiteralInstruction(1) + } +} + diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index 49a5444ab1..a4e4e7189f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -14,14 +14,24 @@ import app.revanced.patches.youtube.layout.theme.lithoColorHookPatch import app.revanced.patches.youtube.layout.theme.lithoColorOverrideHook import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_23_or_greater +import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.util.copyXmlNode +import app.revanced.util.findElementByAttributeValueOrThrow +import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow +import app.revanced.util.inputStreamFromBundledResource +import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference import org.w3c.dom.Element +import java.io.ByteArrayInputStream +import kotlin.use internal var reelTimeBarPlayedColorId = -1L private set @@ -53,7 +63,6 @@ private val seekbarColorResourcePatch = resourcePatch { // Edit the resume playback drawable and replace the progress bar with a custom drawable document("res/drawable/resume_playback_progressbar_drawable.xml").use { document -> - val layerList = document.getElementsByTagName("layer-list").item(0) as Element val progressNode = layerList.getElementsByTagName("item").item(1) as Element if (!progressNode.getAttributeNode("android:id").value.endsWith("progress")) { @@ -66,9 +75,97 @@ private val seekbarColorResourcePatch = resourcePatch { ) scaleNode.replaceChild(replacementNode, shapeNode) } + + + if (is_19_25_or_greater) { + // Add attribute and styles for splash screen custom color. + // Using a style is the only way to selectively change just the seekbar fill color. + // + // Because the style colors must be hard coded for all color possibilities, + // instead of allowing 24 bit color the style is restricted to 8-bit (3-3-2 color depth) + // and the style color closest to the users custom color is used for the splash screen. + arrayOf( + inputStreamFromBundledResource("seekbar/values", "attrs.xml")!! + to "res/values/attrs.xml", + ByteArrayInputStream(create8BitSeekbarColorStyles().toByteArray()) + to "res/values/styles.xml" + ).forEach { (source, destination) -> + "resources".copyXmlNode( + document(source), + document(destination), + ).close() + } + + fun setSplashDrawablePathFillColor(xmlFileNames: Iterable, resourceNames: Iterable) { + xmlFileNames.forEach { xmlFileName -> + document(xmlFileName).use { document -> + resourceNames.forEach { elementId -> + val element = document.childNodes.findElementByAttributeValueOrThrow( + "android:name", + elementId + ) + + val attribute = "android:fillColor" + if (!element.hasAttribute(attribute)) { + throw PatchException("Could not find $attribute for $elementId") + } + + element.setAttribute(attribute, "?attr/splash_custom_seekbar_color") + } + } + } + } + + setSplashDrawablePathFillColor( + listOf( + "res/drawable/\$startup_animation_light__0.xml", + "res/drawable/\$startup_animation_dark__0.xml" + ), + listOf("_R_G_L_10_G_D_0_P_0") + ) + + setSplashDrawablePathFillColor( + listOf( + "res/drawable/\$buenos_aires_animation_light__0.xml", + "res/drawable/\$buenos_aires_animation_dark__0.xml" + ), + listOf("_R_G_L_8_G_D_0_P_0") + ) + } } } +/** + * Generate a style file with all combinations of 3-3-2 (8-bit) colors + */ +private fun create8BitSeekbarColorStyles(): String = StringBuilder().apply { + append("\n\n") + + for (red in 0..7) { + for (green in 0..7) { + for (blue in 0..3) { + val name = "${red}_${green}_${blue}" + + val r = (red * 255 / 7).toString(16).padStart(2, '0') + val g = (green * 255 / 7).toString(16).padStart(2, '0') + val b = (blue * 255 / 3).toString(16).padStart(2, '0') + val color = "#ff$r$g$b" + + append( + """ + + + """.trimIndent() + ) + } + } + } + + append("") +}.toString() + private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/theme/SeekbarColorPatch;" val seekbarColorPatch = bytecodePatch( @@ -139,5 +236,55 @@ val seekbarColorPatch = bytecodePatch( } lithoColorOverrideHook(EXTENSION_CLASS_DESCRIPTOR, "getLithoColor") + + + // region apply seekbar custom color to splash screen animation. + + // Development changes to force the different launch screens to be used. + // All appear to be nearly identical and there is no strong reason to force any particular one. + if (false) { + // 3 combinations of each flag individually on and with both flags off. + launchScreenBuenosAiresFeatureFlagFingerprint.method.returnEarly(false) + launchScreenOptimizedFeatureFlagFingerprint.method.returnEarly(true) + } + + // Don't use the lotte splash screen layout if using custom seekbar. + arrayOf( + launchScreenLayoutTypeFingerprint, + mainActivityOnCreateFingerprint + ).forEach { fingerprint -> + fingerprint.method.apply { + val literalIndex = indexOfFirstLiteralInstructionOrThrow(launchScreenLayoutTypeLotteFeatureFlag) + val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) + val register = getInstruction(resultIndex).registerA + + addInstructions( + resultIndex + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z + move-result v$register + """ + ) + } + } + + // Hook the splash animation drawable to set the a seekbar color theme. + mainActivityOnCreateFingerprint.method.apply { + val drawableIndex = indexOfFirstInstructionOrThrow { + val reference = getReference() + reference?.definingClass == "Landroid/widget/ImageView;" + && reference.name == "getDrawable" + } + val checkCastIndex = indexOfFirstInstructionOrThrow(drawableIndex, Opcode.CHECK_CAST) + val drawableRegister = getInstruction(checkCastIndex).registerA + + addInstruction( + checkCastIndex + 1, + "invoke-static { v$drawableRegister }, $EXTENSION_CLASS_DESCRIPTOR->" + + "setSplashAnimationDrawableTheme(Landroid/graphics/drawable/AnimatedVectorDrawable;)V" + ) + } + + // endregion } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt index 72c81db4c9..f1bb0b37a4 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt @@ -56,7 +56,7 @@ val shortsAutoplayPatch = bytecodePatch( // Main activity is used to check if app is in pip mode. mainActivityOnCreateFingerprint.method.addInstructions( - 0, + 1, "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" + "setMainActivity(Landroid/app/Activity;)V", ) diff --git a/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt index 3faf55a8fa..7a1496afc3 100644 --- a/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt @@ -114,7 +114,6 @@ fun String.copyXmlNode( target: Document, ): AutoCloseable { val hostNodes = source.getElementsByTagName(this).item(0).childNodes - val destinationNode = target.getElementsByTagName(this).item(0) for (index in 0 until hostNodes.length) { diff --git a/patches/src/main/resources/seekbar/values/attrs.xml b/patches/src/main/resources/seekbar/values/attrs.xml new file mode 100644 index 0000000000..2bf349f0d9 --- /dev/null +++ b/patches/src/main/resources/seekbar/values/attrs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 394ab8ea1f47150262b06bc5cd274a1bf3d32ac7 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:37:08 +0400 Subject: [PATCH 2/7] fix: Always round the color down, as a slightly darker color looks better against the lighter seekbar background. --- .../extension/youtube/patches/theme/SeekbarColorPatch.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index 28cd660517..349b7eb998 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -97,9 +97,9 @@ public static boolean useLotteLaunchSplashScreen(boolean original) { private static String get8BitStyleIdentifier(int color24Bit) { // Convert to nearest 3-3-2 bit depth. - final int r3 = Math.round(Color.red(color24Bit) * 7 / 255f); - final int g3 = Math.round(Color.green(color24Bit) * 7 / 255f); - final int b2 = Math.round(Color.blue(color24Bit) * 3 / 255f); + final int r3 = Color.red(color24Bit) * 7 / 255; + final int g3 = Color.green(color24Bit) * 7 / 255; + final int b2 = Color.blue(color24Bit) * 3 / 255; return String.format(Locale.US, "splash_seekbar_color_style_%d_%d_%d", r3, g3, b2); } From 3c1799f73a738808d8657ac26e4b9c6c193e0900 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:59:59 +0400 Subject: [PATCH 3/7] fix 19.16 support, cleanup --- .../layout/seekbar/SeekbarColorPatch.kt | 146 +++++++++--------- 1 file changed, 75 insertions(+), 71 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index a4e4e7189f..26263118b8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -13,7 +13,6 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.youtube.layout.theme.lithoColorHookPatch import app.revanced.patches.youtube.layout.theme.lithoColorOverrideHook import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.playservice.is_19_23_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.settingsPatch @@ -40,6 +39,8 @@ internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L internal var inlineTimeBarPlayedNotHighlightedColorId = -1L private set +internal const val splashSeekbarColorAttributeName = "splash_custom_seekbar_color" + private val seekbarColorResourcePatch = resourcePatch { dependsOn( settingsPatch, @@ -61,7 +62,7 @@ private val seekbarColorResourcePatch = resourcePatch { "inline_time_bar_played_not_highlighted_color", ] - // Edit the resume playback drawable and replace the progress bar with a custom drawable + // Modify the resume playback drawable and replace the progress bar with a custom drawable. document("res/drawable/resume_playback_progressbar_drawable.xml").use { document -> val layerList = document.getElementsByTagName("layer-list").item(0) as Element val progressNode = layerList.getElementsByTagName("item").item(1) as Element @@ -77,61 +78,61 @@ private val seekbarColorResourcePatch = resourcePatch { } - if (is_19_25_or_greater) { - // Add attribute and styles for splash screen custom color. - // Using a style is the only way to selectively change just the seekbar fill color. - // - // Because the style colors must be hard coded for all color possibilities, - // instead of allowing 24 bit color the style is restricted to 8-bit (3-3-2 color depth) - // and the style color closest to the users custom color is used for the splash screen. - arrayOf( - inputStreamFromBundledResource("seekbar/values", "attrs.xml")!! - to "res/values/attrs.xml", - ByteArrayInputStream(create8BitSeekbarColorStyles().toByteArray()) - to "res/values/styles.xml" - ).forEach { (source, destination) -> - "resources".copyXmlNode( - document(source), - document(destination), - ).close() - } + if (!is_19_25_or_greater) { + return@execute + } + + // Add attribute and styles for splash screen custom color. + // Using a style is the only way to selectively change just the seekbar fill color. + // + // Because the style colors must be hard coded for all color possibilities, + // instead of allowing 24 bit color the style is restricted to 8-bit (3-3-2 color depth) + // and the style color closest to the users custom color is used for the splash screen. + arrayOf( + inputStreamFromBundledResource("seekbar/values", "attrs.xml")!! to "res/values/attrs.xml", + ByteArrayInputStream(create8BitSeekbarColorStyles().toByteArray()) to "res/values/styles.xml" + ).forEach { (source, destination) -> + "resources".copyXmlNode( + document(source), + document(destination), + ).close() + } - fun setSplashDrawablePathFillColor(xmlFileNames: Iterable, resourceNames: Iterable) { - xmlFileNames.forEach { xmlFileName -> - document(xmlFileName).use { document -> - resourceNames.forEach { elementId -> - val element = document.childNodes.findElementByAttributeValueOrThrow( - "android:name", - elementId - ) - - val attribute = "android:fillColor" - if (!element.hasAttribute(attribute)) { - throw PatchException("Could not find $attribute for $elementId") - } - - element.setAttribute(attribute, "?attr/splash_custom_seekbar_color") + fun setSplashDrawablePathFillColor(xmlFileNames: Iterable, vararg resourceNames: String) { + xmlFileNames.forEach { xmlFileName -> + document(xmlFileName).use { document -> + resourceNames.forEach { elementId -> + val element = document.childNodes.findElementByAttributeValueOrThrow( + "android:name", + elementId + ) + + val attribute = "android:fillColor" + if (!element.hasAttribute(attribute)) { + throw PatchException("Could not find $attribute for $elementId") } + + element.setAttribute(attribute, "?attr/$splashSeekbarColorAttributeName") } } } - - setSplashDrawablePathFillColor( - listOf( - "res/drawable/\$startup_animation_light__0.xml", - "res/drawable/\$startup_animation_dark__0.xml" - ), - listOf("_R_G_L_10_G_D_0_P_0") - ) - - setSplashDrawablePathFillColor( - listOf( - "res/drawable/\$buenos_aires_animation_light__0.xml", - "res/drawable/\$buenos_aires_animation_dark__0.xml" - ), - listOf("_R_G_L_8_G_D_0_P_0") - ) } + + setSplashDrawablePathFillColor( + listOf( + "res/drawable/\$startup_animation_light__0.xml", + "res/drawable/\$startup_animation_dark__0.xml" + ), + "_R_G_L_10_G_D_0_P_0" + ) + + setSplashDrawablePathFillColor( + listOf( + "res/drawable/\$buenos_aires_animation_light__0.xml", + "res/drawable/\$buenos_aires_animation_dark__0.xml" + ), + "_R_G_L_8_G_D_0_P_0" + ) } } @@ -154,10 +155,9 @@ private fun create8BitSeekbarColorStyles(): String = StringBuilder().apply { append( """ - - """.trimIndent() + """ ) } } @@ -214,28 +214,32 @@ val seekbarColorPatch = bytecodePatch( } } - if (is_19_23_or_greater) { - playerSeekbarGradientConfigFingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG) - val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) - val register = getInstruction(resultIndex).registerA + lithoColorOverrideHook(EXTENSION_CLASS_DESCRIPTOR, "getLithoColor") - addInstructions( - resultIndex + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z - move-result v$register - """, - ) - } + if (!is_19_25_or_greater) { + return@execute + } + + // 19.25+ changes - lithoLinearGradientFingerprint.method.addInstruction( - 0, - "invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->setLinearGradient([I[F)V", + playerSeekbarGradientConfigFingerprint.method.apply { + val literalIndex = indexOfFirstLiteralInstructionOrThrow(PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG) + val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) + val register = getInstruction(resultIndex).registerA + + addInstructions( + resultIndex + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z + move-result v$register + """ ) } - lithoColorOverrideHook(EXTENSION_CLASS_DESCRIPTOR, "getLithoColor") + lithoLinearGradientFingerprint.method.addInstruction( + 0, + "invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->setLinearGradient([I[F)V" + ) // region apply seekbar custom color to splash screen animation. From 066a2eba504f2c6f7050dd7c4f5186b7ddce1f08 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:13:39 +0400 Subject: [PATCH 4/7] fix: use 9-bit color --- .../patches/theme/SeekbarColorPatch.java | 10 ++++----- .../layout/seekbar/SeekbarColorPatch.kt | 21 +++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index 349b7eb998..ea2bcfc3a3 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -95,13 +95,13 @@ public static boolean useLotteLaunchSplashScreen(boolean original) { return original; } - private static String get8BitStyleIdentifier(int color24Bit) { - // Convert to nearest 3-3-2 bit depth. + private static String get9BitStyleIdentifier(int color24Bit) { + // Convert to nearest 9-bit color (3 bits per color channel). final int r3 = Color.red(color24Bit) * 7 / 255; final int g3 = Color.green(color24Bit) * 7 / 255; - final int b2 = Color.blue(color24Bit) * 3 / 255; + final int b3 = Color.blue(color24Bit) * 7 / 255; - return String.format(Locale.US, "splash_seekbar_color_style_%d_%d_%d", r3, g3, b2); + return String.format(Locale.US, "splash_seekbar_color_style_%d_%d_%d", r3, g3, b3); } /** @@ -114,7 +114,7 @@ public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vector // Even if the seekbar color xml value is changed to a completely different color (such as green), // a color filter still cannot be selectively applied when the drawable has more than 1 color. try { - String seekbarStyle = get8BitStyleIdentifier(seekbarColor); + String seekbarStyle = get9BitStyleIdentifier(seekbarColor); Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle); final int styleIdentifierDefault = Utils.getResourceIdentifier( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index 26263118b8..cccb83f63e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -86,11 +86,11 @@ private val seekbarColorResourcePatch = resourcePatch { // Using a style is the only way to selectively change just the seekbar fill color. // // Because the style colors must be hard coded for all color possibilities, - // instead of allowing 24 bit color the style is restricted to 8-bit (3-3-2 color depth) + // instead of allowing 24 bit color the style is restricted to 9-bit (3 bits per color channel) // and the style color closest to the users custom color is used for the splash screen. arrayOf( inputStreamFromBundledResource("seekbar/values", "attrs.xml")!! to "res/values/attrs.xml", - ByteArrayInputStream(create8BitSeekbarColorStyles().toByteArray()) to "res/values/styles.xml" + ByteArrayInputStream(create9BitSeekbarColorStyles().toByteArray()) to "res/values/styles.xml" ).forEach { (source, destination) -> "resources".copyXmlNode( document(source), @@ -137,19 +137,22 @@ private val seekbarColorResourcePatch = resourcePatch { } /** - * Generate a style file with all combinations of 3-3-2 (8-bit) colors + * Generate a style xml with all combinations of 9-bit colors. */ -private fun create8BitSeekbarColorStyles(): String = StringBuilder().apply { - append("\n\n") +private fun create9BitSeekbarColorStyles(): String = StringBuilder().apply { + append("") + append("\n") for (red in 0..7) { for (green in 0..7) { - for (blue in 0..3) { + for (blue in 0..7) { val name = "${red}_${green}_${blue}" - val r = (red * 255 / 7).toString(16).padStart(2, '0') - val g = (green * 255 / 7).toString(16).padStart(2, '0') - val b = (blue * 255 / 3).toString(16).padStart(2, '0') + fun roundTo3BitHex(channel8Bits: Int) = + (channel8Bits * 255 / 7).toString(16).padStart(2, '0') + val r = roundTo3BitHex(red) + val g = roundTo3BitHex(green) + val b = roundTo3BitHex(blue) val color = "#ff$r$g$b" append( From f494e52c9069124988f1dc031d83071a77c2e326 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:40:12 +0400 Subject: [PATCH 5/7] fix 19.46 --- patches/api/patches.api | 1 + .../youtube/layout/seekbar/Fingerprints.kt | 9 +++++++-- .../layout/seekbar/SeekbarColorPatch.kt | 18 +++++++++++------- .../misc/playservice/VersionCheckPatch.kt | 3 +++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/patches/api/patches.api b/patches/api/patches.api index 1bed18e283..88982bc4c5 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1324,6 +1324,7 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat public static final fun is_19_36_or_greater ()Z public static final fun is_19_41_or_greater ()Z public static final fun is_19_43_or_greater ()Z + public static final fun is_19_46_or_greater ()Z } public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt index 6404a3ebbd..e962463b19 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt @@ -54,8 +54,13 @@ internal const val launchScreenLayoutTypeLotteFeatureFlag = 268507948L internal val launchScreenLayoutTypeFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) returns("V") - parameters("Lcom/google/android/apps/youtube/app/watchwhile/MainActivity;", "L", "L", "L", "L", "L", "L", "L") - literal { launchScreenLayoutTypeLotteFeatureFlag } + custom { method, _ -> + val firstParameter = method.parameterTypes.firstOrNull() + // 19.25 - 19.45 + (firstParameter == "Lcom/google/android/apps/youtube/app/watchwhile/MainActivity;" + || firstParameter == "Landroid/app/Activity;") // 19.46+ + && method.containsLiteralInstruction(launchScreenLayoutTypeLotteFeatureFlag) + } } internal val launchScreenOptimizedFeatureFlagFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index cccb83f63e..8262854340 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -14,6 +14,7 @@ import app.revanced.patches.youtube.layout.theme.lithoColorHookPatch import app.revanced.patches.youtube.layout.theme.lithoColorOverrideHook import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater +import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint @@ -126,13 +127,16 @@ private val seekbarColorResourcePatch = resourcePatch { "_R_G_L_10_G_D_0_P_0" ) - setSplashDrawablePathFillColor( - listOf( - "res/drawable/\$buenos_aires_animation_light__0.xml", - "res/drawable/\$buenos_aires_animation_dark__0.xml" - ), - "_R_G_L_8_G_D_0_P_0" - ) + if (!is_19_46_or_greater) { + // Resources removed in 19.46+ + setSplashDrawablePathFillColor( + listOf( + "res/drawable/\$buenos_aires_animation_light__0.xml", + "res/drawable/\$buenos_aires_animation_dark__0.xml" + ), + "_R_G_L_8_G_D_0_P_0" + ) + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt index 0598a77bd9..f989ce16cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt @@ -37,6 +37,8 @@ var is_19_41_or_greater = false private set var is_19_43_or_greater = false private set +var is_19_46_or_greater = false + private set val versionCheckPatch = resourcePatch( description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.", @@ -68,5 +70,6 @@ val versionCheckPatch = resourcePatch( is_19_36_or_greater = 243705000 <= playStoreServicesVersion is_19_41_or_greater = 244305000 <= playStoreServicesVersion is_19_43_or_greater = 244405000 <= playStoreServicesVersion + is_19_46_or_greater = 244705000 <= playStoreServicesVersion } } From f4b538e6f2a3c86660ff122854faae7611c9d0e0 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:12:29 +0400 Subject: [PATCH 6/7] fix: Selectively round up and down depending on the color saturation. --- .../patches/theme/SeekbarColorPatch.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index ea2bcfc3a3..f35c2efc9d 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -95,11 +95,22 @@ public static boolean useLotteLaunchSplashScreen(boolean original) { return original; } + private static int colorChannelTo3Bits(int channel8Bits) { + final float channel3Bits = channel8Bits * 7 / 255f; + + // If a color channel is near zero, then allow rounding up so values between + // 0x12 and 0x23 will show as 0x24. But always round down when the channel is + // near full saturation, otherwise rounding to nearest will cause all values + // between 0xEC and 0xFE to always show as full saturation (0xFF). + return channel3Bits < 6 + ? Math.round(channel3Bits) + : (int) channel3Bits; + } + private static String get9BitStyleIdentifier(int color24Bit) { - // Convert to nearest 9-bit color (3 bits per color channel). - final int r3 = Color.red(color24Bit) * 7 / 255; - final int g3 = Color.green(color24Bit) * 7 / 255; - final int b3 = Color.blue(color24Bit) * 7 / 255; + final int r3 = colorChannelTo3Bits(Color.red(color24Bit)); + final int g3 = colorChannelTo3Bits(Color.green(color24Bit)); + final int b3 = colorChannelTo3Bits(Color.blue(color24Bit)); return String.format(Locale.US, "splash_seekbar_color_style_%d_%d_%d", r3, g3, b3); } From cf65dccfe903ba1a31e40658cadde84c6fc24285 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:34:14 +0400 Subject: [PATCH 7/7] refactor: Remove development code --- .../patches/theme/SeekbarColorPatch.java | 3 ++- .../youtube/layout/seekbar/Fingerprints.kt | 21 ------------------- .../layout/seekbar/SeekbarColorPatch.kt | 9 -------- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index f35c2efc9d..e89086cc68 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -19,7 +19,8 @@ public final class SeekbarColorPatch { private static final boolean SEEKBAR_CUSTOM_COLOR_ENABLED = Settings.SEEKBAR_CUSTOM_COLOR.get(); /** - * Default color of the seekbar. Differs slightly from the custom color used in the settings. + * Default color of the litho seekbar. + * Differs slightly from the default custom seekbar color setting. */ private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000; diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt index e962463b19..ec9fd440cb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt @@ -62,24 +62,3 @@ internal val launchScreenLayoutTypeFingerprint = fingerprint { && method.containsLiteralInstruction(launchScreenLayoutTypeLotteFeatureFlag) } } - -internal val launchScreenOptimizedFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("L") - custom { method, _ -> - method.containsLiteralInstruction(268639016) - && method.containsLiteralInstruction(4) - } -} - -internal val launchScreenBuenosAiresFeatureFlagFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("Z") - parameters("L") - custom { method, _ -> - method.containsLiteralInstruction(268639016) - && method.containsLiteralInstruction(1) - } -} - diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index 8262854340..a2f0797928 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -24,7 +24,6 @@ import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import app.revanced.util.inputStreamFromBundledResource -import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -251,14 +250,6 @@ val seekbarColorPatch = bytecodePatch( // region apply seekbar custom color to splash screen animation. - // Development changes to force the different launch screens to be used. - // All appear to be nearly identical and there is no strong reason to force any particular one. - if (false) { - // 3 combinations of each flag individually on and with both flags off. - launchScreenBuenosAiresFeatureFlagFingerprint.method.returnEarly(false) - launchScreenOptimizedFeatureFlagFingerprint.method.returnEarly(true) - } - // Don't use the lotte splash screen layout if using custom seekbar. arrayOf( launchScreenLayoutTypeFingerprint,