Skip to content

Commit

Permalink
feat(YouTube - Seekbar components): Add Gradient seekbar colors and…
Browse files Browse the repository at this point in the history
… `Gradient seekbar bounds` settings (for YouTube 19.25+)

Remove Cairo start and end color patch options
Rename `Enable Cairo seekbar` to `Enable gradient seekbar`
  • Loading branch information
anddea committed Jan 10, 2025
1 parent 0e29570 commit 2a7b022
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class SeekbarColorPatch {
/**
* If {@link Settings#ENABLE_CUSTOM_SEEKBAR_COLOR} is enabled,
* this is the color value of {@link Settings#ENABLE_CUSTOM_SEEKBAR_COLOR_VALUE}.
* Otherwise this is {@link #ORIGINAL_SEEKBAR_COLOR}.
* Otherwise, this is {@link #ORIGINAL_SEEKBAR_COLOR}.
*/
private static int seekbarColor = ORIGINAL_SEEKBAR_COLOR;

Expand Down Expand Up @@ -187,6 +187,71 @@ public static void setLinearGradient(int[] colors, float[] positions) {
}
}

/**
* Injection point
* <p>
* Overrides default colors for gradient seekbar
*/
public static void setSeekbarGradientColors(int[] colors) {
try {
String[] colorStrings = Settings.GRADIENT_SEEKBAR_COLORS.get().split(",");
if (colorStrings.length != 2) {
throw new Exception("Invalid color format. Need two colors separated by a comma.");
}
int[] newColors = new int[2];

for (int i = 0; i < 2; i++) {
try {
// Parse hex color string to int
newColors[i] = Color.parseColor(colorStrings[i].trim());
} catch (IllegalArgumentException ex) {
Utils.showToastShort(str("revanced_custom_seekbar_color_value_invalid_invalid_toast"));
Settings.GRADIENT_SEEKBAR_COLORS.resetToDefault();
return;
}
}

System.arraycopy(newColors, 0, colors, 0, colors.length);
} catch (Exception ex) {
Logger.printException(() -> "setSeekbarGradientPositions failure", ex);
}
}

// Set seekbar thumb color
// The seekbar thumb was initially set to the gradient seekbar's starting color.
// But we will switch to using the end color.
public static int setSeekbarThumbColor() {
String[] colorStrings = Settings.GRADIENT_SEEKBAR_COLORS.get().split(",");
return Color.parseColor(colorStrings[1].trim());
}

/**
* Injection point
* <p>
* Overrides default positions for gradient seekbar
*/
public static void setSeekbarGradientPositions(float[] positions) {
try {
String[] positionStrings = Settings.GRADIENT_SEEKBAR_POSITIONS.get().split(",");
float[] newPositions = new float[positions.length];

for (int i = 0; i < positions.length; i++) {
float position = Float.parseFloat(positionStrings[i].trim());
// Verify if position is within valid range
if (position < 0.0f || position > 1.0f) {
Utils.showToastShort(str("revanced_gradient_seekbar_positions_reset"));
Settings.GRADIENT_SEEKBAR_POSITIONS.resetToDefault();
return;
}
newPositions[i] = position;
}

System.arraycopy(newPositions, 0, positions, 0, positions.length);
} catch (Exception ex) {
Logger.printException(() -> "setSeekbarGradientPositions failure", ex);
}
}

/**
* Injection point.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ public class Settings extends BaseSettings {
PatchStatus.OldSeekbarThumbnailsDefaultBoolean(), true);
public static final BooleanSetting ENABLE_SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_enable_seekbar_thumbnails_high_quality", FALSE, true, "revanced_enable_seekbar_thumbnails_high_quality_dialog_message");
public static final BooleanSetting ENABLE_CAIRO_SEEKBAR = new BooleanSetting("revanced_enable_cairo_seekbar", FALSE, true);
public static final StringSetting GRADIENT_SEEKBAR_COLORS = new StringSetting("revanced_gradient_seekbar_colors", "#FF0033, #FF2791", true, parent(ENABLE_CAIRO_SEEKBAR));
public static final StringSetting GRADIENT_SEEKBAR_POSITIONS = new StringSetting("revanced_gradient_seekbar_positions", "0.7, 1.0", parent(ENABLE_CAIRO_SEEKBAR));

// PreferenceScreen: Player - Video description
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,43 @@ internal val lithoLinearGradientFingerprint = legacyFingerprint(
)
internal const val launchScreenLayoutTypeLotteFeatureFlag = 268507948L

internal val setBoundsFingerprint = legacyFingerprint(
name = "setBoundsFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("I", "I", "I", "I"),
opcodes = listOf(
Opcode.NEW_ARRAY,
Opcode.FILL_ARRAY_DATA
)
)

internal val gradientSeekbarConstructorFingerprint = legacyFingerprint(
name = "gradientSeekbarConstructorFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
returnType = "V",
parameters = listOf("Landroid/content/Context;"),
opcodes = listOf(
Opcode.MOVE_RESULT,
Opcode.FILLED_NEW_ARRAY,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IPUT_OBJECT
)
)

internal val seekbarThumbFingerprint = legacyFingerprint(
name = "seekbarThumbFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
returnType = "V",
parameters = listOf("Landroid/content/Context;"),
opcodes = listOf(
Opcode.CONST,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.INVOKE_VIRTUAL
)
)

internal val launchScreenLayoutTypeFingerprint = legacyFingerprint(
name = "launchScreenLayoutTypeFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.drawable.addDrawableColorHook
Expand Down Expand Up @@ -48,10 +48,8 @@ import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.resolvable
import app.revanced.util.inputStreamFromBundledResource
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
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.instruction.*
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import org.w3c.dom.Element
import java.io.ByteArrayInputStream
Expand Down Expand Up @@ -112,20 +110,6 @@ val seekbarComponentsPatch = bytecodePatch(
versionCheckPatch,
)

val cairoStartColor by stringOption(
key = "cairoStartColor",
default = "#FFFF0033",
title = "Cairo start color",
description = "Set Cairo start color for the seekbar."
)

val cairoEndColor by stringOption(
key = "cairoEndColor",
default = "#FFFF2791",
title = "Cairo end color",
description = "Set Cairo end color for the seekbar."
)

execute {

val restoreOldSplashAnimationIncluded = CUSTOM_BRANDING_ICON_FOR_YOUTUBE.included == true &&
Expand Down Expand Up @@ -296,7 +280,7 @@ val seekbarComponentsPatch = bytecodePatch(
}
}

// Hook the splash animation drawable to set the a seekbar color theme.
// Hook the splash animation drawable to set the seekbar color theme.
onCreateMethod.apply {
val drawableIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
Expand All @@ -314,7 +298,6 @@ val seekbarComponentsPatch = bytecodePatch(
"setSplashAnimationDrawableTheme(Landroid/graphics/drawable/AnimatedVectorDrawable;)V"
)
}

}

val context = getContext()
Expand All @@ -334,19 +317,6 @@ val seekbarComponentsPatch = bytecodePatch(
scaleNode.replaceChild(replacementNode, shapeNode)
}

context.document("res/values/colors.xml").use { document ->
document.doRecursively loop@{ node ->
if (node is Element && node.tagName == "color") {
if (node.getAttribute("name") == "yt_youtube_red_cairo") {
node.textContent = cairoStartColor
}
if (node.getAttribute("name") == "yt_youtube_magenta") {
node.textContent = cairoEndColor
}
}
}
}

if (is_19_25_or_greater && !restoreOldSplashAnimationIncluded) {
// Add attribute and styles for splash screen custom color.
// Using a style is the only way to selectively change just the seekbar fill color.
Expand Down Expand Up @@ -521,6 +491,97 @@ val seekbarComponentsPatch = bytecodePatch(

// endregion

// region patch for gradient seekbar color and bounds

if (is_19_25_or_greater) {
// In version 19.23, gradient colors and bounds use a different structure,
// and the implementation is still raw/underdeveloped.

// Adjust gradient seekbar colors
gradientSeekbarConstructorFingerprint.methodOrThrow().apply {
val instructions = implementation!!.instructions.toList()

val iputCInstructionIndex = instructions.indexOfLast {
it.opcode == Opcode.IPUT
}

if (iputCInstructionIndex == -1) {
throw PatchException("Could not find IPUT instruction")
}

val registerUsedInIput = (instructions[iputCInstructionIndex] as? OneRegisterInstruction)?.registerA
?: throw PatchException("Could not get register used in IPUT instruction")

val nextFreeRegister1 = registerUsedInIput + 1
val nextFreeRegister2 = registerUsedInIput + 3 // +2 is going to be used, so skip it to avoid errors

val putValueInstructionIndex = instructions.indexOfLast { it.opcode == Opcode.IPUT_OBJECT }

if (putValueInstructionIndex == -1) {
throw PatchException("Could not find last IPUT_OBJECT instruction in constructor")
}

val fieldReference = instructions[putValueInstructionIndex].getReference<FieldReference>()
?: throw PatchException("Could not get field reference for IPUT_OBJECT")

val smaliInstruction = """
const/4 v$registerUsedInIput, 0x2
new-array v$registerUsedInIput, v$registerUsedInIput, [I
const v$nextFreeRegister1, -0xFF0033
const/4 v$nextFreeRegister2, 0x0
aput v$nextFreeRegister1, v$registerUsedInIput, v$nextFreeRegister2
const v$nextFreeRegister1, -0xFF2791
const/4 v$nextFreeRegister2, 0x1
aput v$nextFreeRegister1, v$registerUsedInIput, v$nextFreeRegister2
invoke-static { v$registerUsedInIput }, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->setSeekbarGradientColors([I)V
iput-object v$registerUsedInIput, p0, ${fieldReference.definingClass}->${fieldReference.name}:${fieldReference.type}
""".trimIndent()

addInstructions(iputCInstructionIndex + 1, smaliInstruction)
removeInstructions(putValueInstructionIndex + 2, 9)
}

// Adjust gradient seekbar positions
setBoundsFingerprint.methodOrThrow().apply {
val newArrayIndex = indexOfFirstInstructionOrThrow(Opcode.NEW_ARRAY)
val arrayRegister = getInstruction<OneRegisterInstruction>(newArrayIndex).registerA

val smaliInstruction = """
invoke-static/range { v$arrayRegister }, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->setSeekbarGradientPositions([F)V
""".trimIndent()

addInstruction(
indexOfFirstInstructionOrThrow(Opcode.FILL_ARRAY_DATA) + 1,
smaliInstruction
)
}

// Set seekbar thumb color
seekbarThumbFingerprint.methodOrThrow().apply {
val instructions = implementation!!.instructions.toList()

val lastMoveResultIndex = instructions.indexOfLast { it.opcode == Opcode.MOVE_RESULT }

if (lastMoveResultIndex == -1) {
throw PatchException("Could not find the last move-result instruction")
}

val resultRegister = (instructions[lastMoveResultIndex] as? OneRegisterInstruction)?.registerA
?: throw PatchException("Could not get the register used in the last move-result instruction")

val smaliInstruction = """
invoke-static {}, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->setSeekbarThumbColor()I
move-result v$resultRegister
""".trimIndent()

addInstructions(lastMoveResultIndex + 1, smaliInstruction)
}

settingArray += "SETTINGS: GRADIENT_SEEKBAR_OPTIONS"
}

// endregion patch for gradient seekbar color and bounds

// region add settings

addPreference(settingArray, SEEKBAR_COMPONENTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,11 @@ Note:
• Video playback will use more internet data than VP9.
• VP9 codec is still used for HDR video."</string>
<string name="revanced_disable_vp9_codec_title">Disable VP9 codec</string>
<string name="revanced_enable_cairo_seekbar_summary_off">Cairo seekbar is disabled.</string>
<string name="revanced_enable_cairo_seekbar_summary_on">"Cairo seekbar is enabled.
<string name="revanced_enable_cairo_seekbar_summary_off">Gradient seekbar is disabled.</string>
<string name="revanced_enable_cairo_seekbar_summary_on">"Gradient seekbar is enabled.

Side effect: Cairo theme is also applied to notification dots."</string>
<string name="revanced_enable_cairo_seekbar_title">Enable Cairo seekbar</string>
<string name="revanced_enable_cairo_seekbar_title">Enable gradient seekbar</string>
<string name="revanced_enable_compact_controls_overlay_summary_off">Controls overlay fills the fullscreen.</string>
<string name="revanced_enable_compact_controls_overlay_summary_on">Controls overlay does not fill the fullscreen.</string>
<string name="revanced_enable_compact_controls_overlay_title">Enable compact controls overlay</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@
<!-- SETTINGS: ENABLE_CAIRO_SEEKBAR
<PreferenceCategory android:title="@string/revanced_preference_category_experimental_flag" android:layout="@layout/revanced_settings_preferences_category"/>
<SwitchPreference android:title="@string/revanced_enable_cairo_seekbar_title" android:key="revanced_enable_cairo_seekbar" android:summaryOn="@string/revanced_enable_cairo_seekbar_summary_on" android:summaryOff="@string/revanced_enable_cairo_seekbar_summary_off" />SETTINGS: ENABLE_CAIRO_SEEKBAR -->
<!-- SETTINGS: GRADIENT_SEEKBAR_OPTIONS
<app.revanced.extension.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_gradient_seekbar_colors_title" android:key="revanced_gradient_seekbar_colors" android:summary="@string/revanced_gradient_seekbar_colors_summary" android:inputType="text" android:dependency="revanced_enable_cairo_seekbar" />
<app.revanced.extension.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_gradient_seekbar_positions_title" android:key="revanced_gradient_seekbar_positions" android:summary="@string/revanced_gradient_seekbar_positions_summary" android:inputType="text" android:dependency="revanced_enable_cairo_seekbar" />SETTINGS: GRADIENT_SEEKBAR_OPTIONS -->

<!-- SETTINGS: SEEKBAR_COMPONENTS
</PreferenceScreen>SETTINGS: SEEKBAR_COMPONENTS -->
Expand Down

0 comments on commit 2a7b022

Please sign in to comment.