mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-08 10:34:33 +02:00
fix(YouTube - Seekbar components): Custom seekbar color
not applied to gradient seekbar in YouTube 19.34.42
This commit is contained in:
parent
231f897bcc
commit
2bd7b5aeed
@ -6,7 +6,7 @@ public class DrawableColorPatch {
|
|||||||
-14606047 // comments box background
|
-14606047 // comments box background
|
||||||
};
|
};
|
||||||
|
|
||||||
public static int getColor(int originalValue) {
|
public static int getLithoColor(int originalValue) {
|
||||||
if (anyEquals(originalValue, DARK_VALUES))
|
if (anyEquals(originalValue, DARK_VALUES))
|
||||||
return -16777215;
|
return -16777215;
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import static app.revanced.extension.youtube.utils.ExtendedUtils.validateValue;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -597,12 +596,26 @@ public class PlayerPatch {
|
|||||||
return !Settings.HIDE_PLAYER_FLYOUT_MENU_PIP.get();
|
return !Settings.HIDE_PLAYER_FLYOUT_MENU_PIP.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overriding this values is possible only after the litho component has been loaded.
|
||||||
|
* Otherwise, crash will occur.
|
||||||
|
* See {@link InitializationPatch#onCreate}.
|
||||||
|
*
|
||||||
|
* @param original original value.
|
||||||
|
* @return whether to enable Sleep timer Mode in the player flyout menu.
|
||||||
|
*/
|
||||||
|
public static boolean hideDeprecatedSleepTimerMenu(boolean original) {
|
||||||
|
if (!BaseSettings.SETTINGS_INITIALIZED.get()) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !Settings.HIDE_PLAYER_FLYOUT_MENU_SLEEP_TIMER.get();
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region [Seekbar components] patch
|
// region [Seekbar components] patch
|
||||||
|
|
||||||
public static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000;
|
|
||||||
|
|
||||||
public static String appendTimeStampInformation(String original) {
|
public static String appendTimeStampInformation(String original) {
|
||||||
if (!Settings.APPEND_TIME_STAMP_INFORMATION.get()) return original;
|
if (!Settings.APPEND_TIME_STAMP_INFORMATION.get()) return original;
|
||||||
|
|
||||||
@ -641,48 +654,6 @@ public class PlayerPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getSeekbarClickedColorValue(final int colorValue) {
|
|
||||||
return colorValue == ORIGINAL_SEEKBAR_COLOR
|
|
||||||
? overrideSeekbarColor(colorValue)
|
|
||||||
: colorValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int resumedProgressBarColor(final int colorValue) {
|
|
||||||
return Settings.ENABLE_CUSTOM_SEEKBAR_COLOR.get()
|
|
||||||
? getSeekbarClickedColorValue(colorValue)
|
|
||||||
: colorValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all drawable color that use the YouTube seekbar color.
|
|
||||||
* Used only for the video thumbnails seekbar.
|
|
||||||
* <p>
|
|
||||||
* If {@link Settings#HIDE_SEEKBAR_THUMBNAIL} is enabled, this returns a fully transparent color.
|
|
||||||
*/
|
|
||||||
public static int getColor(int colorValue) {
|
|
||||||
if (colorValue == ORIGINAL_SEEKBAR_COLOR) {
|
|
||||||
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
|
|
||||||
return 0x00000000;
|
|
||||||
}
|
|
||||||
return overrideSeekbarColor(ORIGINAL_SEEKBAR_COLOR);
|
|
||||||
}
|
|
||||||
return colorValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Points where errors occur when playing videos on the PlayStore (ROOT Build)
|
|
||||||
*/
|
|
||||||
public static int overrideSeekbarColor(final int colorValue) {
|
|
||||||
try {
|
|
||||||
return Settings.ENABLE_CUSTOM_SEEKBAR_COLOR.get()
|
|
||||||
? Color.parseColor(Settings.ENABLE_CUSTOM_SEEKBAR_COLOR_VALUE.get())
|
|
||||||
: colorValue;
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
Settings.ENABLE_CUSTOM_SEEKBAR_COLOR_VALUE.resetToDefault();
|
|
||||||
}
|
|
||||||
return colorValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean enableSeekbarTapping() {
|
public static boolean enableSeekbarTapping() {
|
||||||
return Settings.ENABLE_SEEKBAR_TAPPING.get();
|
return Settings.ENABLE_SEEKBAR_TAPPING.get();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,260 @@
|
|||||||
|
package app.revanced.extension.youtube.patches.player;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.utils.StringRef.str;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.utils.Logger;
|
||||||
|
import app.revanced.extension.shared.utils.ResourceUtils;
|
||||||
|
import app.revanced.extension.shared.utils.Utils;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class SeekbarColorPatch {
|
||||||
|
|
||||||
|
private static final boolean CUSTOM_SEEKBAR_COLOR_ENABLED =
|
||||||
|
Settings.ENABLE_CUSTOM_SEEKBAR_COLOR.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default color of the litho seekbar.
|
||||||
|
* Differs slightly from the default custom seekbar color setting.
|
||||||
|
*/
|
||||||
|
private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default colors of the gradient seekbar.
|
||||||
|
*/
|
||||||
|
private static final int[] ORIGINAL_SEEKBAR_GRADIENT_COLORS = { 0xFFFF0033, 0xFFFF2791 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default positions of the gradient seekbar.
|
||||||
|
*/
|
||||||
|
private static final float[] ORIGINAL_SEEKBAR_GRADIENT_POSITIONS = { 0.8f, 1.0f };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default YouTube seekbar color brightness.
|
||||||
|
*/
|
||||||
|
private static final float ORIGINAL_SEEKBAR_COLOR_BRIGHTNESS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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}.
|
||||||
|
*/
|
||||||
|
private static int seekbarColor = ORIGINAL_SEEKBAR_COLOR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom seekbar hue, saturation, and brightness values.
|
||||||
|
*/
|
||||||
|
private static final float[] customSeekbarColorHSV = new float[3];
|
||||||
|
|
||||||
|
static {
|
||||||
|
float[] hsv = new float[3];
|
||||||
|
Color.colorToHSV(ORIGINAL_SEEKBAR_COLOR, hsv);
|
||||||
|
ORIGINAL_SEEKBAR_COLOR_BRIGHTNESS = hsv[2];
|
||||||
|
|
||||||
|
if (CUSTOM_SEEKBAR_COLOR_ENABLED) {
|
||||||
|
loadCustomSeekbarColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadCustomSeekbarColor() {
|
||||||
|
try {
|
||||||
|
seekbarColor = Color.parseColor(Settings.ENABLE_CUSTOM_SEEKBAR_COLOR_VALUE.get());
|
||||||
|
Color.colorToHSV(seekbarColor, customSeekbarColorHSV);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Utils.showToastShort(str("revanced_custom_seekbar_color_value_invalid_invalid_toast"));
|
||||||
|
Utils.showToastShort(str("revanced_extended_reset_to_default_toast"));
|
||||||
|
Settings.ENABLE_CUSTOM_SEEKBAR_COLOR_VALUE.resetToDefault();
|
||||||
|
loadCustomSeekbarColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSeekbarColor() {
|
||||||
|
return seekbarColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point
|
||||||
|
*/
|
||||||
|
public static boolean playerSeekbarGradientEnabled(boolean original) {
|
||||||
|
if (CUSTOM_SEEKBAR_COLOR_ENABLED) return false;
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point
|
||||||
|
*/
|
||||||
|
public static boolean useLotteLaunchSplashScreen(boolean original) {
|
||||||
|
Logger.printDebug(() -> "useLotteLaunchSplashScreen original: " + original);
|
||||||
|
|
||||||
|
if (CUSTOM_SEEKBAR_COLOR_ENABLED) return false;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = get9BitStyleIdentifier(seekbarColor);
|
||||||
|
Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle);
|
||||||
|
|
||||||
|
final int styleIdentifierDefault = ResourceUtils.getStyleIdentifier(seekbarStyle);
|
||||||
|
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.
|
||||||
|
* <p>
|
||||||
|
* Overrides all Litho components that use the YouTube seekbar color.
|
||||||
|
* Used only for the video thumbnails seekbar.
|
||||||
|
* <p>
|
||||||
|
* If {@link Settings#HIDE_SEEKBAR_THUMBNAIL} is enabled, this returns a fully transparent color.
|
||||||
|
*/
|
||||||
|
public static int getLithoColor(int colorValue) {
|
||||||
|
if (colorValue == ORIGINAL_SEEKBAR_COLOR) {
|
||||||
|
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
|
||||||
|
return 0x00000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR);
|
||||||
|
}
|
||||||
|
return colorValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void setLinearGradient(int[] colors, float[] positions) {
|
||||||
|
final boolean hideSeekbar = Settings.HIDE_SEEKBAR_THUMBNAIL.get();
|
||||||
|
|
||||||
|
if (CUSTOM_SEEKBAR_COLOR_ENABLED || hideSeekbar) {
|
||||||
|
// Most litho usage of linear gradients is hooked here,
|
||||||
|
// so must only change if the values are those for the seekbar.
|
||||||
|
if (Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
|
||||||
|
&& Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions)) {
|
||||||
|
Arrays.fill(colors, hideSeekbar
|
||||||
|
? 0x00000000
|
||||||
|
: seekbarColor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.printDebug(() -> "Ignoring gradient colors: " + Arrays.toString(colors)
|
||||||
|
+ " positions: " + Arrays.toString(positions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
* <p>
|
||||||
|
* Overrides color when video player seekbar is clicked.
|
||||||
|
*/
|
||||||
|
public static int getVideoPlayerSeekbarClickedColor(int colorValue) {
|
||||||
|
if (!CUSTOM_SEEKBAR_COLOR_ENABLED) {
|
||||||
|
return colorValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return colorValue == ORIGINAL_SEEKBAR_COLOR
|
||||||
|
? getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR)
|
||||||
|
: colorValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
* <p>
|
||||||
|
* Overrides color used for the video player seekbar.
|
||||||
|
*/
|
||||||
|
public static int getVideoPlayerSeekbarColor(int originalColor) {
|
||||||
|
if (!CUSTOM_SEEKBAR_COLOR_ENABLED) {
|
||||||
|
return originalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSeekbarColorValue(originalColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color parameter is changed to the custom seekbar color, while retaining
|
||||||
|
* the brightness and alpha changes of the parameter value compared to the original seekbar color.
|
||||||
|
*/
|
||||||
|
private static int getSeekbarColorValue(int originalColor) {
|
||||||
|
try {
|
||||||
|
if (!CUSTOM_SEEKBAR_COLOR_ENABLED || originalColor == seekbarColor) {
|
||||||
|
return originalColor; // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
final int alphaDifference = Color.alpha(originalColor) - Color.alpha(ORIGINAL_SEEKBAR_COLOR);
|
||||||
|
|
||||||
|
// The seekbar uses the same color but different brightness for different situations.
|
||||||
|
float[] hsv = new float[3];
|
||||||
|
Color.colorToHSV(originalColor, hsv);
|
||||||
|
final float brightnessDifference = hsv[2] - ORIGINAL_SEEKBAR_COLOR_BRIGHTNESS;
|
||||||
|
|
||||||
|
// Apply the brightness difference to the custom seekbar color.
|
||||||
|
hsv[0] = customSeekbarColorHSV[0];
|
||||||
|
hsv[1] = customSeekbarColorHSV[1];
|
||||||
|
hsv[2] = clamp(customSeekbarColorHSV[2] + brightnessDifference, 0, 1);
|
||||||
|
|
||||||
|
final int replacementAlpha = clamp(Color.alpha(seekbarColor) + alphaDifference, 0, 255);
|
||||||
|
final int replacementColor = Color.HSVToColor(replacementAlpha, hsv);
|
||||||
|
Logger.printDebug(() -> String.format("Original color: #%08X replacement color: #%08X",
|
||||||
|
originalColor, replacementColor));
|
||||||
|
return replacementColor;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "getSeekbarColorValue failure", ex);
|
||||||
|
return originalColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @noinspection SameParameterValue */
|
||||||
|
private static int clamp(int value, int lower, int upper) {
|
||||||
|
return Math.max(lower, Math.min(value, upper));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @noinspection SameParameterValue */
|
||||||
|
private static float clamp(float value, float lower, float upper) {
|
||||||
|
return Math.max(lower, Math.min(value, upper));
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ public class DrawableColorPatch {
|
|||||||
private static int whiteColor = 0;
|
private static int whiteColor = 0;
|
||||||
private static int blackColor = 0;
|
private static int blackColor = 0;
|
||||||
|
|
||||||
public static int getColor(int originalValue) {
|
public static int getLithoColor(int originalValue) {
|
||||||
if (anyEquals(originalValue, DARK_VALUES)) {
|
if (anyEquals(originalValue, DARK_VALUES)) {
|
||||||
return getBlackColor();
|
return getBlackColor();
|
||||||
} else if (anyEquals(originalValue, WHITE_VALUES)) {
|
} else if (anyEquals(originalValue, WHITE_VALUES)) {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package app.revanced.extension.youtube.patches.utils;
|
package app.revanced.extension.youtube.patches.utils;
|
||||||
|
|
||||||
import static app.revanced.extension.youtube.patches.player.PlayerPatch.ORIGINAL_SEEKBAR_COLOR;
|
|
||||||
import static app.revanced.extension.youtube.patches.player.PlayerPatch.resumedProgressBarColor;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.ColorFilter;
|
import android.graphics.ColorFilter;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
@ -12,6 +9,7 @@ import android.graphics.drawable.Drawable;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.youtube.patches.player.SeekbarColorPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@ -24,7 +22,7 @@ public class ProgressBarDrawable extends Drawable {
|
|||||||
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
|
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paint.setColor(resumedProgressBarColor(ORIGINAL_SEEKBAR_COLOR));
|
paint.setColor(SeekbarColorPatch.getSeekbarColor());
|
||||||
canvas.drawRect(getBounds(), paint);
|
canvas.drawRect(getBounds(), paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ val amoledPatch = resourcePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addDrawableColorHook("$UTILS_PATH/DrawableColorPatch;->getColor(I)I")
|
addDrawableColorHook("$UTILS_PATH/DrawableColorPatch;->getLithoColor(I)I")
|
||||||
|
|
||||||
document("res/values/colors.xml").use { document ->
|
document("res/values/colors.xml").use { document ->
|
||||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||||
|
@ -24,7 +24,7 @@ val sharedThemePatch = resourcePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addDrawableColorHook("$UTILS_PATH/DrawableColorPatch;->getColor(I)I")
|
addDrawableColorHook("$UTILS_PATH/DrawableColorPatch;->getLithoColor(I)I")
|
||||||
|
|
||||||
// edit the resource files to change the splash screen color
|
// edit the resource files to change the splash screen color
|
||||||
val attrsResourceFile = "res/values/attrs.xml"
|
val attrsResourceFile = "res/values/attrs.xml"
|
||||||
|
@ -1,11 +1,42 @@
|
|||||||
package app.revanced.patches.youtube.player.seekbar
|
package app.revanced.patches.youtube.player.seekbar
|
||||||
|
|
||||||
import app.revanced.patches.youtube.utils.resourceid.reelTimeBarPlayedColor
|
import app.revanced.patches.youtube.utils.resourceid.reelTimeBarPlayedColor
|
||||||
|
import app.revanced.util.containsLiteralInstruction
|
||||||
import app.revanced.util.fingerprint.legacyFingerprint
|
import app.revanced.util.fingerprint.legacyFingerprint
|
||||||
import app.revanced.util.or
|
import app.revanced.util.or
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L
|
||||||
|
|
||||||
|
internal val playerSeekbarGradientConfigFingerprint = legacyFingerprint(
|
||||||
|
name = "playerSeekbarGradientConfigFingerprint",
|
||||||
|
returnType = "Z",
|
||||||
|
parameters = emptyList(),
|
||||||
|
literals = listOf(PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG),
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val lithoLinearGradientFingerprint = legacyFingerprint(
|
||||||
|
name = "lithoLinearGradientFingerprint",
|
||||||
|
accessFlags = AccessFlags.STATIC.value,
|
||||||
|
returnType = "Landroid/graphics/LinearGradient;",
|
||||||
|
parameters = listOf("F", "F", "F", "F", "[I", "[F")
|
||||||
|
)
|
||||||
|
internal const val launchScreenLayoutTypeLotteFeatureFlag = 268507948L
|
||||||
|
|
||||||
|
internal val launchScreenLayoutTypeFingerprint = legacyFingerprint(
|
||||||
|
name = "launchScreenLayoutTypeFingerprint",
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
returnType = "V",
|
||||||
|
customFingerprint = { 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 controlsOverlayStyleFingerprint = legacyFingerprint(
|
internal val controlsOverlayStyleFingerprint = legacyFingerprint(
|
||||||
name = "controlsOverlayStyleFingerprint",
|
name = "controlsOverlayStyleFingerprint",
|
||||||
opcodes = listOf(Opcode.CONST_HIGH16),
|
opcodes = listOf(Opcode.CONST_HIGH16),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package app.revanced.patches.youtube.player.seekbar
|
package app.revanced.patches.youtube.player.seekbar
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
@ -9,14 +10,19 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
import app.revanced.patches.shared.drawable.addDrawableColorHook
|
import app.revanced.patches.shared.drawable.addDrawableColorHook
|
||||||
import app.revanced.patches.shared.drawable.drawableColorHookPatch
|
import app.revanced.patches.shared.drawable.drawableColorHookPatch
|
||||||
|
import app.revanced.patches.shared.mainactivity.onCreateMethod
|
||||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||||
import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
|
import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
|
||||||
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_CLASS_DESCRIPTOR
|
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_CLASS_DESCRIPTOR
|
||||||
|
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_PATH
|
||||||
import app.revanced.patches.youtube.utils.flyoutmenu.flyoutMenuHookPatch
|
import app.revanced.patches.youtube.utils.flyoutmenu.flyoutMenuHookPatch
|
||||||
|
import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
|
||||||
import app.revanced.patches.youtube.utils.patch.PatchList.SEEKBAR_COMPONENTS
|
import app.revanced.patches.youtube.utils.patch.PatchList.SEEKBAR_COMPONENTS
|
||||||
import app.revanced.patches.youtube.utils.playerButtonsResourcesFingerprint
|
import app.revanced.patches.youtube.utils.playerButtonsResourcesFingerprint
|
||||||
import app.revanced.patches.youtube.utils.playerButtonsVisibilityFingerprint
|
import app.revanced.patches.youtube.utils.playerButtonsVisibilityFingerprint
|
||||||
import app.revanced.patches.youtube.utils.playerSeekbarColorFingerprint
|
import app.revanced.patches.youtube.utils.playerSeekbarColorFingerprint
|
||||||
|
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
|
||||||
|
import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater
|
||||||
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarColorizedBarPlayedColorDark
|
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarColorizedBarPlayedColorDark
|
||||||
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarPlayedNotHighlightedColor
|
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarPlayedNotHighlightedColor
|
||||||
@ -29,6 +35,8 @@ import app.revanced.patches.youtube.utils.settings.ResourceUtils.getContext
|
|||||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||||
import app.revanced.patches.youtube.utils.totalTimeFingerprint
|
import app.revanced.patches.youtube.utils.totalTimeFingerprint
|
||||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||||
|
import app.revanced.util.copyXmlNode
|
||||||
|
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||||
import app.revanced.util.findMethodsOrThrow
|
import app.revanced.util.findMethodsOrThrow
|
||||||
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
|
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
|
||||||
import app.revanced.util.fingerprint.matchOrThrow
|
import app.revanced.util.fingerprint.matchOrThrow
|
||||||
@ -38,6 +46,7 @@ import app.revanced.util.getReference
|
|||||||
import app.revanced.util.getWalkerMethod
|
import app.revanced.util.getWalkerMethod
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||||
|
import app.revanced.util.inputStreamFromBundledResource
|
||||||
import app.revanced.util.updatePatchStatus
|
import app.revanced.util.updatePatchStatus
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
@ -46,6 +55,45 @@ 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.TwoRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
|
internal const val splashSeekbarColorAttributeName = "splash_custom_seekbar_color"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a style xml with all combinations of 9-bit colors.
|
||||||
|
*/
|
||||||
|
private fun create9BitSeekbarColorStyles(): String = StringBuilder().apply {
|
||||||
|
append("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
|
||||||
|
append("<resources>\n")
|
||||||
|
|
||||||
|
for (red in 0..7) {
|
||||||
|
for (green in 0..7) {
|
||||||
|
for (blue in 0..7) {
|
||||||
|
val name = "${red}_${green}_${blue}"
|
||||||
|
|
||||||
|
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(
|
||||||
|
"""
|
||||||
|
<style name="splash_seekbar_color_style_$name">
|
||||||
|
<item name="$splashSeekbarColorAttributeName">$color</item>
|
||||||
|
</style>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append("</resources>")
|
||||||
|
}.toString()
|
||||||
|
|
||||||
|
private const val EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR =
|
||||||
|
"$PLAYER_PATH/SeekbarColorPatch;"
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val seekbarComponentsPatch = bytecodePatch(
|
val seekbarComponentsPatch = bytecodePatch(
|
||||||
@ -57,6 +105,7 @@ val seekbarComponentsPatch = bytecodePatch(
|
|||||||
dependsOn(
|
dependsOn(
|
||||||
drawableColorHookPatch,
|
drawableColorHookPatch,
|
||||||
flyoutMenuHookPatch,
|
flyoutMenuHookPatch,
|
||||||
|
mainActivityResolvePatch,
|
||||||
sharedResourceIdPatch,
|
sharedResourceIdPatch,
|
||||||
settingsPatch,
|
settingsPatch,
|
||||||
videoInformationPatch,
|
videoInformationPatch,
|
||||||
@ -157,26 +206,25 @@ val seekbarComponentsPatch = bytecodePatch(
|
|||||||
|
|
||||||
// region patch for seekbar color
|
// region patch for seekbar color
|
||||||
|
|
||||||
fun MutableMethod.hookSeekbarColor(literal: Long) {
|
fun MutableMethod.addColorChangeInstructions(literal: Long) {
|
||||||
val insertIndex = indexOfFirstLiteralInstructionOrThrow(literal) + 2
|
val insertIndex = indexOfFirstLiteralInstructionOrThrow(literal) + 2
|
||||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
insertIndex + 1, """
|
insertIndex + 1, """
|
||||||
invoke-static {v$insertRegister}, $PLAYER_CLASS_DESCRIPTOR->overrideSeekbarColor(I)I
|
invoke-static {v$insertRegister}, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I
|
||||||
move-result v$insertRegister
|
move-result v$insertRegister
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
playerSeekbarColorFingerprint.methodOrThrow().apply {
|
playerSeekbarColorFingerprint.methodOrThrow().apply {
|
||||||
hookSeekbarColor(inlineTimeBarColorizedBarPlayedColorDark)
|
addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDark)
|
||||||
hookSeekbarColor(inlineTimeBarPlayedNotHighlightedColor)
|
addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
shortsSeekbarColorFingerprint.methodOrThrow().apply {
|
shortsSeekbarColorFingerprint.methodOrThrow().apply {
|
||||||
hookSeekbarColor(reelTimeBarPlayedColor)
|
addColorChangeInstructions(reelTimeBarPlayedColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
controlsOverlayStyleFingerprint.matchOrThrow().let {
|
controlsOverlayStyleFingerprint.matchOrThrow().let {
|
||||||
@ -187,16 +235,68 @@ val seekbarComponentsPatch = bytecodePatch(
|
|||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
0, """
|
0, """
|
||||||
invoke-static {v$colorRegister}, $PLAYER_CLASS_DESCRIPTOR->getSeekbarClickedColorValue(I)I
|
invoke-static {v$colorRegister}, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->getVideoPlayerSeekbarClickedColor(I)I
|
||||||
move-result v$colorRegister
|
move-result v$colorRegister
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addDrawableColorHook("$PLAYER_CLASS_DESCRIPTOR->getColor(I)I")
|
addDrawableColorHook("$EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->getLithoColor(I)I")
|
||||||
|
|
||||||
getContext().document("res/drawable/resume_playback_progressbar_drawable.xml")
|
if (is_19_25_or_greater) {
|
||||||
|
playerSeekbarGradientConfigFingerprint.injectLiteralInstructionBooleanCall(
|
||||||
|
PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z"
|
||||||
|
)
|
||||||
|
|
||||||
|
lithoLinearGradientFingerprint.methodOrThrow().addInstruction(
|
||||||
|
0,
|
||||||
|
"invoke-static/range { p4 .. p5 }, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->setLinearGradient([I[F)V"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Don't use the lotte splash screen layout if using custom seekbar.
|
||||||
|
arrayOf(
|
||||||
|
launchScreenLayoutTypeFingerprint.methodOrThrow(),
|
||||||
|
onCreateMethod
|
||||||
|
).forEach { method ->
|
||||||
|
method.apply {
|
||||||
|
val literalIndex = indexOfFirstLiteralInstructionOrThrow(launchScreenLayoutTypeLotteFeatureFlag)
|
||||||
|
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
resultIndex + 1,
|
||||||
|
"""
|
||||||
|
invoke-static { v$register }, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z
|
||||||
|
move-result v$register
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook the splash animation drawable to set the a seekbar color theme.
|
||||||
|
onCreateMethod.apply {
|
||||||
|
val drawableIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.definingClass == "Landroid/widget/ImageView;" &&
|
||||||
|
reference.name == "getDrawable"
|
||||||
|
}
|
||||||
|
val checkCastIndex = indexOfFirstInstructionOrThrow(drawableIndex, Opcode.CHECK_CAST)
|
||||||
|
val drawableRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
checkCastIndex + 1,
|
||||||
|
"invoke-static { v$drawableRegister }, $EXTENSION_SEEKBAR_COLOR_CLASS_DESCRIPTOR->" +
|
||||||
|
"setSplashAnimationDrawableTheme(Landroid/graphics/drawable/AnimatedVectorDrawable;)V"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val context = getContext()
|
||||||
|
|
||||||
|
context.document("res/drawable/resume_playback_progressbar_drawable.xml")
|
||||||
.use { document ->
|
.use { document ->
|
||||||
val layerList = document.getElementsByTagName("layer-list").item(0) as Element
|
val layerList = document.getElementsByTagName("layer-list").item(0) as Element
|
||||||
val progressNode = layerList.getElementsByTagName("item").item(1) as Element
|
val progressNode = layerList.getElementsByTagName("item").item(1) as Element
|
||||||
@ -211,6 +311,63 @@ val seekbarComponentsPatch = bytecodePatch(
|
|||||||
scaleNode.replaceChild(replacementNode, shapeNode)
|
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 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("youtube/seekbar/values", "attrs.xml")!! to "res/values/attrs.xml",
|
||||||
|
ByteArrayInputStream(create9BitSeekbarColorStyles().toByteArray()) to "res/values/styles.xml"
|
||||||
|
).forEach { (source, destination) ->
|
||||||
|
"resources".copyXmlNode(
|
||||||
|
context.document(source),
|
||||||
|
context.document(destination),
|
||||||
|
).close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSplashDrawablePathFillColor(xmlFileNames: Iterable<String>, vararg resourceNames: String) {
|
||||||
|
xmlFileNames.forEach { xmlFileName ->
|
||||||
|
context.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"
|
||||||
|
),
|
||||||
|
"_R_G_L_10_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"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region patch for high quality thumbnails
|
// region patch for high quality thumbnails
|
||||||
|
@ -43,6 +43,8 @@ var is_19_43_or_greater = false
|
|||||||
private set
|
private set
|
||||||
var is_19_44_or_greater = false
|
var is_19_44_or_greater = false
|
||||||
private set
|
private set
|
||||||
|
var is_19_46_or_greater = false
|
||||||
|
private set
|
||||||
|
|
||||||
val versionCheckPatch = resourcePatch(
|
val versionCheckPatch = resourcePatch(
|
||||||
description = "versionCheckPatch",
|
description = "versionCheckPatch",
|
||||||
@ -77,5 +79,6 @@ val versionCheckPatch = resourcePatch(
|
|||||||
is_19_41_or_greater = 244305000 <= playStoreServicesVersion
|
is_19_41_or_greater = 244305000 <= playStoreServicesVersion
|
||||||
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
|
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
|
||||||
is_19_44_or_greater = 244505000 <= playStoreServicesVersion
|
is_19_44_or_greater = 244505000 <= playStoreServicesVersion
|
||||||
|
is_19_46_or_greater = 244705000 <= playStoreServicesVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<attr format="reference|color" name="splash_custom_seekbar_color"/>
|
||||||
|
</resources>
|
@ -1112,6 +1112,7 @@ Tap and hold to toggle the appended information type."</string>
|
|||||||
<string name="revanced_enable_custom_seekbar_color_summary_off">Custom seekbar color is disabled.</string>
|
<string name="revanced_enable_custom_seekbar_color_summary_off">Custom seekbar color is disabled.</string>
|
||||||
<string name="revanced_custom_seekbar_color_value_title">Custom seekbar color value</string>
|
<string name="revanced_custom_seekbar_color_value_title">Custom seekbar color value</string>
|
||||||
<string name="revanced_custom_seekbar_color_value_summary">Type the hex code of the seekbar color.</string>
|
<string name="revanced_custom_seekbar_color_value_summary">Type the hex code of the seekbar color.</string>
|
||||||
|
<string name="revanced_custom_seekbar_color_value_invalid_invalid_toast">Invalid seekbar color value.</string>
|
||||||
<string name="revanced_enable_seekbar_tapping_title">Enable seekbar tapping</string>
|
<string name="revanced_enable_seekbar_tapping_title">Enable seekbar tapping</string>
|
||||||
<string name="revanced_enable_seekbar_tapping_summary_on">Seekbar tapping is enabled.</string>
|
<string name="revanced_enable_seekbar_tapping_summary_on">Seekbar tapping is enabled.</string>
|
||||||
<string name="revanced_enable_seekbar_tapping_summary_off">Seekbar tapping is disabled.</string>
|
<string name="revanced_enable_seekbar_tapping_summary_off">Seekbar tapping is disabled.</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user