feat(YouTube - Swipe controls): Add separate color settings for the brightness and volume bars (#5043)

This commit is contained in:
MarcaD 2025-05-28 12:54:28 +03:00 committed by GitHub
parent d4827e118f
commit 80f50e8c50
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 159 additions and 112 deletions

View File

@ -337,13 +337,17 @@ public class Settings extends BaseSettings {
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final StringSetting SWIPE_OVERLAY_PROGRESS_COLOR = new StringSetting("revanced_swipe_overlay_progress_color", "#FFFFFF", true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final StringSetting SWIPE_OVERLAY_BRIGHTNESS_COLOR = new StringSetting("revanced_swipe_overlay_progress_brightness_color", "#FFFFFF", true,
parent(SWIPE_BRIGHTNESS));
public static final StringSetting SWIPE_OVERLAY_VOLUME_COLOR = new StringSetting("revanced_swipe_overlay_progress_volume_color", "#FFFFFF", true,
parent(SWIPE_VOLUME));
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true,
parent(SWIPE_BRIGHTNESS));
public static final FloatSetting SWIPE_BRIGHTNESS_VALUE = new FloatSetting("revanced_swipe_brightness_value", -1f);
public static final BooleanSetting SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS = new BooleanSetting("revanced_swipe_lowest_value_enable_auto_brightness", FALSE, true, parent(SWIPE_BRIGHTNESS));
public static final BooleanSetting SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS = new BooleanSetting("revanced_swipe_lowest_value_enable_auto_brightness", FALSE, true,
parent(SWIPE_BRIGHTNESS));
// ReturnYoutubeDislike
public static final BooleanSetting RYD_ENABLED = new BooleanSetting("revanced_ryd_enabled", TRUE);

View File

@ -1,10 +1,10 @@
package app.revanced.extension.youtube.swipecontrols
import android.annotation.SuppressLint
import android.graphics.Color
import app.revanced.extension.shared.Logger
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils
import app.revanced.extension.shared.settings.StringSetting
import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType
@ -51,105 +51,112 @@ class SwipeControlsConfigurationProvider {
/**
* Indicates whether press-to-swipe mode is enabled, requiring a press before swiping to activate controls.
*/
val shouldEnablePressToSwipe: Boolean
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
val shouldEnablePressToSwipe = Settings.SWIPE_PRESS_TO_ENGAGE.get()
/**
* The threshold for detecting swipe gestures, in pixels.
* Loaded once to ensure consistent behavior during rapid scroll events.
*/
val swipeMagnitudeThreshold: Int
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
val swipeMagnitudeThreshold = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
/**
* The sensitivity of volume swipe gestures, determining how much volume changes per swipe.
* Resets to default if set to 0, as it would disable swiping.
*/
val volumeSwipeSensitivity: Int
get() {
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
val volumeSwipeSensitivity: Int by lazy {
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
if (sensitivity < 1) {
return Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
}
return sensitivity
if (sensitivity < 1) {
return@lazy Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
}
sensitivity
}
//endregion
//region overlay adjustments
/**
* Indicates whether haptic feedback should be enabled for swipe control interactions.
*/
val shouldEnableHapticFeedback: Boolean
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get()
val shouldEnableHapticFeedback = Settings.SWIPE_HAPTIC_FEEDBACK.get()
/**
* The duration in milliseconds that the overlay should remain visible after a change.
*/
val overlayShowTimeoutMillis: Long
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
val overlayShowTimeoutMillis = Settings.SWIPE_OVERLAY_TIMEOUT.get()
/**
* The background opacity of the overlay, converted from a percentage (0-100) to an alpha value (0-255).
* Resets to default and shows a toast if the value is out of range.
*/
val overlayBackgroundOpacity: Int
get() {
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
val overlayBackgroundOpacity: Int by lazy {
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
opacity = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
}
opacity = opacity * 255 / 100
return Color.argb(opacity, 0, 0, 0)
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
opacity = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
}
opacity = opacity * 255 / 100
Color.argb(opacity, 0, 0, 0)
}
/**
* The color of the progress bar in the overlay.
* The color of the progress bar in the overlay for brightness.
* Resets to default and shows a toast if the color string is invalid or empty.
*/
val overlayProgressColor: Int
get() {
try {
@SuppressLint("UseKtx")
val color = Color.parseColor(Settings.SWIPE_OVERLAY_PROGRESS_COLOR.get())
return (0xBF000000.toInt() or (color and 0xFFFFFF))
} catch (ex: IllegalArgumentException) {
Logger.printDebug({ "Could not parse color" }, ex)
Utils.showToastLong(str("revanced_swipe_overlay_progress_color_invalid_toast"))
Settings.SWIPE_OVERLAY_PROGRESS_COLOR.resetToDefault()
return overlayProgressColor // Recursively return.
}
val overlayBrightnessProgressColor: Int by lazy {
// Use lazy to avoid repeat parsing. Changing color requires app restart.
getSettingColor(Settings.SWIPE_OVERLAY_BRIGHTNESS_COLOR)
}
/**
* The color of the progress bar in the overlay for volume.
* Resets to default and shows a toast if the color string is invalid or empty.
*/
val overlayVolumeProgressColor: Int by lazy {
getSettingColor(Settings.SWIPE_OVERLAY_VOLUME_COLOR)
}
private fun getSettingColor(setting: StringSetting): Int {
try {
//noinspection UseKtx
val color = Color.parseColor(setting.get())
return (0xBF000000.toInt() or (color and 0x00FFFFFF))
} catch (ex: IllegalArgumentException) {
// This code should never be reached.
// Color picker rejects and will not save bad colors to a setting.
// If a user imports bad data, the color picker preference resets the
// bad color before this method can be called.
Logger.printDebug({ "Could not parse color: $setting" }, ex)
Utils.showToastLong(str("revanced_settings_color_invalid"))
setting.resetToDefault()
return getSettingColor(setting) // Recursively return.
}
}
/**
* The background color used for the filled portion of the progress bar in the overlay.
*/
val overlayFillBackgroundPaint: Int
get() = 0x80D3D3D3.toInt()
val overlayFillBackgroundPaint = 0x80D3D3D3.toInt()
/**
* The color used for text and icons in the overlay.
*/
val overlayTextColor: Int
get() = Color.WHITE
val overlayTextColor = Color.WHITE
/**
* The text size in the overlay, in density-independent pixels (dp).
* Must be between 1 and 30 dp; resets to default and shows a toast if invalid.
*/
val overlayTextSize: Int
get() {
val size = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
if (size < 1 || size > 30) {
Utils.showToastLong(str("revanced_swipe_text_overlay_size_invalid_toast"))
return Settings.SWIPE_OVERLAY_TEXT_SIZE.resetToDefault()
}
return size
val overlayTextSize: Int by lazy {
val size = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
if (size < 1 || size > 30) {
Utils.showToastLong(str("revanced_swipe_text_overlay_size_invalid_toast"))
return@lazy Settings.SWIPE_OVERLAY_TEXT_SIZE.resetToDefault()
}
size
}
/**
* Defines the style of the swipe controls overlay, determining its layout and appearance.
@ -199,28 +206,25 @@ class SwipeControlsConfigurationProvider {
/**
* A minimal vertical progress bar.
*/
VERTICAL_MINIMAL(isMinimal = true, isVertical = true)
VERTICAL_MINIMAL(isMinimal = true, isVertical = true)
}
/**
* The current style of the overlay, determining its layout and appearance.
*/
val overlayStyle: SwipeOverlayStyle
get() = Settings.SWIPE_OVERLAY_STYLE.get()
val overlayStyle = Settings.SWIPE_OVERLAY_STYLE.get()
//endregion
//region behaviour
/**
* Indicates whether the brightness level should be saved and restored when entering or exiting fullscreen mode.
*/
val shouldSaveAndRestoreBrightness: Boolean
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
val shouldSaveAndRestoreBrightness = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
/**
* Indicates whether auto-brightness should be enabled when the brightness gesture reaches its lowest value.
*/
val shouldLowestValueEnableAutoBrightness: Boolean
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
val shouldLowestValueEnableAutoBrightness = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
/**
* The saved brightness value for the swipe gesture, used to restore brightness in fullscreen mode.
@ -229,4 +233,4 @@ class SwipeControlsConfigurationProvider {
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value)
//endregion
}
}

View File

@ -39,7 +39,7 @@ class SwipeControlsOverlayLayout(
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider())
// Drawable icons for brightness and volume
// Drawable icons for brightness and volume.
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
private val lowBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_low")
private val mediumBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_medium")
@ -50,7 +50,7 @@ class SwipeControlsOverlayLayout(
private val normalVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_normal")
private val fullVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_high")
// Function to retrieve drawable resources by name
// Function to retrieve drawable resources by name.
private fun getDrawable(name: String): Drawable {
val drawable = resources.getDrawable(
Utils.getResourceIdentifier(context, name, "drawable"),
@ -60,19 +60,19 @@ class SwipeControlsOverlayLayout(
return drawable
}
// Initialize progress bars
// Initialize progress bars.
private val circularProgressView: CircularProgressView
private val horizontalProgressView: HorizontalProgressView
private val verticalBrightnessProgressView: VerticalProgressView
private val verticalVolumeProgressView: VerticalProgressView
init {
// Initialize circular progress bar
// Initialize circular progress bar.
circularProgressView = CircularProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayBrightnessProgressColor, // Placeholder, updated in showFeedbackView.
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
@ -80,18 +80,18 @@ class SwipeControlsOverlayLayout(
layoutParams = LayoutParams(100f.toDisplayPixels().toInt(), 100f.toDisplayPixels().toInt()).apply {
addRule(CENTER_IN_PARENT, TRUE)
}
visibility = GONE // Initially hidden
visibility = GONE // Initially hidden.
}
addView(circularProgressView)
// Initialize horizontal progress bar
// Initialize horizontal progress bar.
val screenWidth = resources.displayMetrics.widthPixels
val layoutWidth = (screenWidth * 4 / 5).toInt() // Cap at ~360dp
val layoutWidth = (screenWidth * 4 / 5).toInt() // Cap at ~360dp.
horizontalProgressView = HorizontalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayBrightnessProgressColor, // Placeholder, updated in showFeedbackView.
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
@ -104,16 +104,16 @@ class SwipeControlsOverlayLayout(
topMargin = 20f.toDisplayPixels().toInt()
}
}
visibility = GONE // Initially hidden
visibility = GONE // Initially hidden.
}
addView(horizontalProgressView)
// Initialize vertical progress bar for brightness (right side)
// Initialize vertical progress bar for brightness (right side).
verticalBrightnessProgressView = VerticalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayBrightnessProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
@ -123,16 +123,16 @@ class SwipeControlsOverlayLayout(
rightMargin = 40f.toDisplayPixels().toInt()
addRule(CENTER_VERTICAL)
}
visibility = GONE // Initially hidden
visibility = GONE // Initially hidden.
}
addView(verticalBrightnessProgressView)
// Initialize vertical progress bar for volume (left side)
// Initialize vertical progress bar for volume (left side).
verticalVolumeProgressView = VerticalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayVolumeProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
@ -142,12 +142,12 @@ class SwipeControlsOverlayLayout(
leftMargin = 40f.toDisplayPixels().toInt()
addRule(CENTER_VERTICAL)
}
visibility = GONE // Initially hidden
visibility = GONE // Initially hidden.
}
addView(verticalVolumeProgressView)
}
// Handler and callback for hiding progress bars
// Handler and callback for hiding progress bars.
private val feedbackHideHandler = Handler(Looper.getMainLooper())
private val feedbackHideCallback = Runnable {
circularProgressView.visibility = GONE
@ -165,29 +165,42 @@ class SwipeControlsOverlayLayout(
val viewToShow = when {
config.overlayStyle.isCircular -> circularProgressView
config.overlayStyle.isVertical -> if (isBrightness) verticalBrightnessProgressView else verticalVolumeProgressView
config.overlayStyle.isVertical ->
if (isBrightness)
verticalBrightnessProgressView
else
verticalVolumeProgressView
else -> horizontalProgressView
}
viewToShow.apply {
// Set the appropriate progress color.
if (this is CircularProgressView || this is HorizontalProgressView) {
setProgressColor(
if (isBrightness)
config.overlayBrightnessProgressColor
else
config.overlayVolumeProgressColor
)
}
setProgress(progress, max, value, isBrightness)
this.icon = icon
visibility = VISIBLE
}
}
// Handle volume change
// Handle volume change.
override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) {
val volumePercentage = (newVolume.toFloat() / maximumVolume) * 100
val icon = when {
newVolume == 0 -> mutedVolumeIcon
volumePercentage < 33 -> lowVolumeIcon
volumePercentage < 66 -> normalVolumeIcon
volumePercentage < 25 -> lowVolumeIcon
volumePercentage < 50 -> normalVolumeIcon
else -> fullVolumeIcon
}
showFeedbackView("$newVolume", newVolume, maximumVolume, icon, isBrightness = false)
}
// Handle brightness change
// Handle brightness change.
override fun onBrightnessChanged(brightness: Double) {
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
val displayText = if (config.overlayStyle.isVertical) "А"
@ -195,18 +208,19 @@ class SwipeControlsOverlayLayout(
showFeedbackView(displayText, 0, 100, autoBrightnessIcon, isBrightness = true)
} else {
val brightnessValue = round(brightness).toInt()
val clampedProgress = max(0, brightnessValue)
val icon = when {
brightnessValue < 25 -> lowBrightnessIcon
brightnessValue < 50 -> mediumBrightnessIcon
brightnessValue < 75 -> highBrightnessIcon
clampedProgress < 25 -> lowBrightnessIcon
clampedProgress < 50 -> mediumBrightnessIcon
clampedProgress < 75 -> highBrightnessIcon
else -> fullBrightnessIcon
}
val displayText = if (config.overlayStyle.isVertical) "$brightnessValue" else "$brightnessValue%"
showFeedbackView(displayText, brightnessValue, 100, icon, isBrightness = true)
val displayText = if (config.overlayStyle.isVertical) "$clampedProgress" else "$clampedProgress%"
showFeedbackView(displayText, clampedProgress, 100, icon, isBrightness = true)
}
}
// Begin swipe session
// Begin swipe session.
override fun onEnterSwipeSession() {
if (config.shouldEnableHapticFeedback) {
@Suppress("DEPRECATION")
@ -233,25 +247,41 @@ abstract class AbstractProgressView(
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
// Combined paint creation function for both fill and stroke styles
private fun createPaint(color: Int, style: Paint.Style = Paint.Style.FILL, strokeCap: Paint.Cap = Paint.Cap.BUTT, strokeWidth: Float = 0f) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
// Combined paint creation function for both fill and stroke styles.
private fun createPaint(
color: Int,
style: Paint.Style = Paint.Style.FILL,
strokeCap: Paint.Cap = Paint.Cap.BUTT,
strokeWidth: Float = 0f
) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
this.style = style
this.color = color
this.strokeCap = strokeCap
this.strokeWidth = strokeWidth
}
// Initialize paints
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 6f.toDisplayPixels())
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor
// Initialize paints.
val backgroundPaint = createPaint(
overlayBackgroundOpacity,
style = Paint.Style.FILL
)
val progressPaint = createPaint(
overlayProgressColor,
style = Paint.Style.STROKE,
strokeCap = Paint.Cap.ROUND,
strokeWidth = 6f.toDisplayPixels()
)
val fillBackgroundPaint = createPaint(
overlayFillBackgroundPaint,
style = Paint.Style.FILL
)
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor
textAlign = Paint.Align.CENTER
textSize = overlayTextSize.toFloat().toDisplayPixels()
textSize = overlayTextSize.toFloat().toDisplayPixels()
}
// Rect for text measurement
// Rect for text measurement.
protected val textBounds = Rect()
protected var progress = 0
@ -268,13 +298,18 @@ abstract class AbstractProgressView(
invalidate()
}
fun setProgressColor(color: Int) {
progressPaint.color = color
invalidate()
}
protected fun measureTextWidth(text: String, paint: Paint): Int {
paint.getTextBounds(text, 0, text.length, textBounds)
return textBounds.width()
}
override fun onDraw(canvas: Canvas) {
// Base class implementation can be empty
// Base class implementation can be empty.
}
}
@ -393,8 +428,8 @@ class HorizontalProgressView(
}
/**
* Calculate required width based on content
* @return Required width to display all elements
* Calculate required width based on content.
* @return Required width to display all elements.
*/
private fun calculateRequiredWidth(): Float {
textWidth = measureTextWidth(displayText, textPaint).toFloat()
@ -537,8 +572,8 @@ class VerticalProgressView(
}
/**
* Calculate required height based on content
* @return Required height to display all elements
* Calculate required height based on content.
* @return Required height to display all elements.
*/
private fun calculateRequiredHeight(): Float {
return if (!isMinimalStyle) {
@ -633,4 +668,4 @@ class VerticalProgressView(
super.setProgress(value, max, text, isBrightnessMode)
requestLayout()
}
}
}

View File

@ -48,7 +48,10 @@ private val swipeControlsResourcePatch = resourcePatch {
summaryKey = null,
),
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_progress_color",
TextPreference("revanced_swipe_overlay_progress_brightness_color",
tag = "app.revanced.extension.shared.settings.preference.ColorPickerPreference",
inputType = InputType.TEXT_CAP_CHARACTERS),
TextPreference("revanced_swipe_overlay_progress_volume_color",
tag = "app.revanced.extension.shared.settings.preference.ColorPickerPreference",
inputType = InputType.TEXT_CAP_CHARACTERS),
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),

View File

@ -553,9 +553,10 @@ Adjust volume by swiping vertically on the right side of the screen"</string>
<string name="revanced_swipe_overlay_background_opacity_title">Swipe overlay background opacity</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Opacity value between 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Swipe opacity must be between 0-100</string>
<string name="revanced_swipe_overlay_progress_color_title">Swipe overlay progress bar color</string>
<string name="revanced_swipe_overlay_progress_color_summary">The color of the progress bar for volume and brightness controls</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Invalid progress bar color</string>
<string name="revanced_swipe_overlay_progress_brightness_color_title">Swipe overlay brightness color</string>
<string name="revanced_swipe_overlay_progress_brightness_color_summary">The color of the progress bar for brightness controls</string>
<string name="revanced_swipe_overlay_progress_volume_color_title">Swipe overlay volume color</string>
<string name="revanced_swipe_overlay_progress_volume_color_summary">The color of the progress bar for volume controls</string>
<string name="revanced_swipe_text_overlay_size_title">Swipe overlay text size</string>
<string name="revanced_swipe_text_overlay_size_summary">The text size for swipe overlay between 1-30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">The text size must be between 1-30</string>