@@ -73,11 +73,16 @@ import androidx.compose.ui.draw.drawWithCache
7373import androidx.compose.ui.draw.drawWithContent
7474import androidx.compose.ui.geometry.CornerRadius
7575import androidx.compose.ui.geometry.Offset
76+ import androidx.compose.ui.geometry.RoundRect
7677import androidx.compose.ui.geometry.Size
78+ import androidx.compose.ui.graphics.Brush
7779import androidx.compose.ui.graphics.Color
7880import androidx.compose.ui.graphics.ColorFilter
81+ import androidx.compose.ui.graphics.Outline
82+ import androidx.compose.ui.graphics.Path
7983import androidx.compose.ui.graphics.asImageBitmap
8084import androidx.compose.ui.graphics.drawscope.DrawScope
85+ import androidx.compose.ui.graphics.drawscope.clipPath
8186import androidx.compose.ui.graphics.drawscope.scale
8287import androidx.compose.ui.graphics.drawscope.translate
8388import androidx.compose.ui.graphics.painter.BitmapPainter
@@ -164,6 +169,9 @@ fun BrightnessSlider(
164169 else -> Dimensions .SliderTrackRoundedCorner
165170 }
166171
172+ val trackShape = RoundedCornerShape (trackCornerDp)
173+ val brightnessGradient = brightnessSliderGradient()
174+
167175 var value by remember(gammaValue) { mutableIntStateOf(gammaValue) }
168176 val animatedValue by
169177 animateFloatAsState(targetValue = value.toFloat(), label = " BrightnessSliderAnimatedValue" )
@@ -188,7 +196,7 @@ fun BrightnessSlider(
188196 } else {
189197 null
190198 }
191- val colors = colors()
199+ val colors = colors(brightnessGradient )
192200
193201 // The value state is recreated every time gammaValue changes, so we recreate this derivedState
194202 // We have to use value as that's the value that changes when the user is dragging (gammaValue
@@ -359,9 +367,31 @@ fun BrightnessSlider(
359367 BrightnessSliderMotionTestKeys .InactiveIconAlpha
360368 }
361369 .height(TrackHeight )
362- .drawWithContent {
370+ .drawWithCache {
371+
372+ val outline = trackShape.createOutline(size, layoutDirection, this )
373+ val clipPath = outline.asPath()
374+
375+ onDrawWithContent {
363376 drawContent()
364377
378+ val gradient = brightnessGradient
379+ if (gradient != null ) {
380+ val gapPx = ThumbTrackGapSize .toPx()
381+ val fraction = sliderState.coercedValueAsFraction
382+ val activeEnd = (size.width * fraction - gapPx).coerceAtLeast(0f )
383+
384+ if (activeEnd > 0f ) {
385+ clipPath(clipPath) {
386+ drawRect(
387+ brush = gradient.brush,
388+ topLeft = Offset .Zero ,
389+ size = Size (activeEnd.coerceAtMost(size.width), size.height)
390+ )
391+ }
392+ }
393+ }
394+
365395 val yOffset = size.height / 2 - IconSize .toSize().height / 2
366396 val activeTrackStart = 0f
367397 val activeTrackEnd =
@@ -393,6 +423,7 @@ fun BrightnessSlider(
393423 iconActiveAlphaAnimatable.value,
394424 )
395425 }
426+ }
396427 },
397428 trackCornerSize = trackCornerDp,
398429 trackInsideCornerSize = 2 .dp,
@@ -425,6 +456,14 @@ fun BrightnessSlider(
425456 }
426457}
427458
459+ fun Outline.asPath (): Path {
460+ return when (this ) {
461+ is Outline .Generic -> path
462+ is Outline .Rounded -> Path ().apply { addRoundRect(roundRect) }
463+ is Outline .Rectangle -> Path ().apply { addRect(rect) }
464+ }
465+ }
466+
428467@Composable
429468fun rememberSliderShapeMode (): Int {
430469 val context = LocalContext .current
@@ -465,6 +504,62 @@ fun rememberSliderShapeMode(): Int {
465504 return shapeMode
466505}
467506
507+ private data class BrightnessGradient (
508+ val brush : Brush ,
509+ )
510+
511+ @Composable
512+ private fun rememberSliderGradient (): Boolean {
513+ val context = LocalContext .current
514+ val contentResolver = context.contentResolver
515+
516+ fun readEnabled (): Boolean {
517+ return try {
518+ Settings .System .getIntForUser(
519+ contentResolver, Settings .System .QS_BRIGHTNESS_SLIDER_GRADIENT , 0 ,
520+ UserHandle .USER_CURRENT
521+ ) != 0
522+ } catch (_: Throwable ) {
523+ false
524+ }
525+ }
526+
527+ var enabled by remember { mutableStateOf(readEnabled()) }
528+
529+ DisposableEffect (contentResolver) {
530+ val observer = object : ContentObserver (null ) {
531+ override fun onChange (selfChange : Boolean ) {
532+ enabled = readEnabled()
533+ }
534+ }
535+
536+ contentResolver.registerContentObserver(
537+ Settings .System .getUriFor(Settings .System .QS_BRIGHTNESS_SLIDER_GRADIENT ),
538+ false , observer, UserHandle .USER_ALL
539+ )
540+
541+ onDispose {
542+ contentResolver.unregisterContentObserver(observer)
543+ }
544+ }
545+
546+ return enabled
547+ }
548+
549+ @Composable
550+ private fun brightnessSliderGradient (): BrightnessGradient ? {
551+ if (! rememberSliderGradient()) return null
552+
553+ val colors = listOf (
554+ MaterialTheme .colorScheme.primary,
555+ MaterialTheme .colorScheme.secondary
556+ )
557+
558+ return BrightnessGradient (
559+ brush = Brush .horizontalGradient(colors)
560+ )
561+ }
562+
468563private fun Modifier.sliderBackground (color : Color , corner : Dp ) = drawWithCache {
469564 val offsetAround = SliderBackgroundFrameSize .toSize()
470565 val newSize = Size (size.width + 2 * offsetAround.width, size.height + 2 * offsetAround.height)
@@ -719,9 +814,11 @@ object BrightnessSliderMotionTestKeys {
719814}
720815
721816@Composable
722- private fun colors (): SliderColors {
723- return SliderDefaults .colors()
817+ private fun colors (brightnessGradient : BrightnessGradient ? ): SliderColors {
818+ val base = SliderDefaults .colors()
819+ return base
724820 .copy(
821+ activeTrackColor = if (brightnessGradient != null ) Color .Transparent else base.activeTrackColor,
725822 inactiveTrackColor = LocalAndroidColorScheme .current.surfaceEffect1,
726823 activeTickColor = MaterialTheme .colorScheme.onPrimary,
727824 inactiveTickColor = MaterialTheme .colorScheme.onSurface,
0 commit comments