Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0562d0e authored by Anton Potapov's avatar Anton Potapov
Browse files

Don't animate Volume Panel initial state

When the Volume Panel opens all the sliders update simultaneously. This
puts a lot of pressure on Compose, specially given that the recent
updates made those animations more elaborate.

Flag: com.android.systemui.volume_redesign
Fixes: 403737312
Test: atest VolumePanelScreenshotTest
Test: manual on the foldable. Open and close volume panel with increased
animation scale

Change-Id: I82895614566d44b4479b708765e68edec6ea748e
parent d3af2e14
Loading
Loading
Loading
Loading
+68 −60
Original line number Diff line number Diff line
@@ -31,8 +31,10 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -130,6 +132,11 @@ fun VolumeSlider(
                    disabledActiveTickColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                    disabledInactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                )
            val sliderHeight = 52.dp
            if (state is SliderState.Empty) {
                // reserve the space for the slider to avoid excess resizing
                Spacer(modifier = Modifier.weight(1f).height(sliderHeight))
            } else {
                Slider(
                    value = state.value,
                    valueRange = state.valueRange,
@@ -178,7 +185,7 @@ fun VolumeSlider(
                            interactionSource = interactionSource,
                            enabled = state.isEnabled,
                            colors = materialSliderColors,
                        thumbSize = DpSize(4.dp, 52.dp),
                            thumbSize = DpSize(4.dp, sliderHeight),
                        )
                    },
                    haptics =
@@ -189,8 +196,9 @@ fun VolumeSlider(
                                orientation = Orientation.Horizontal,
                            )
                        } ?: Haptics.Disabled,
                modifier = Modifier.weight(1f).sysuiResTag(state.label),
                    modifier = Modifier.weight(1f).height(sliderHeight).sysuiResTag(state.label),
                )
            }
            button?.invoke(this)
        }
        state.disabledMessage?.let { disabledMessage ->
+24 −21
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ fun SliderTrack(
) {
    val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
    val measurePolicy =
        remember(sliderState) {
        remember(sliderState, isRtl, isVertical, thumbTrackGapSize) {
            TrackMeasurePolicy(
                sliderState = sliderState,
                shouldMirrorIcons = !isVertical && isRtl || isVertical,
@@ -102,28 +102,28 @@ fun SliderTrack(
                contents = Contents.Active.TrackStartIcon,
                isEnabled = isEnabled,
                colors = colors,
                state = measurePolicy,
                trackMeasurePolicy = measurePolicy,
            )
            TrackIcon(
                icon = activeTrackEndIcon,
                contents = Contents.Active.TrackEndIcon,
                isEnabled = isEnabled,
                colors = colors,
                state = measurePolicy,
                trackMeasurePolicy = measurePolicy,
            )
            TrackIcon(
                icon = inactiveTrackStartIcon,
                contents = Contents.Inactive.TrackStartIcon,
                isEnabled = isEnabled,
                colors = colors,
                state = measurePolicy,
                trackMeasurePolicy = measurePolicy,
            )
            TrackIcon(
                icon = inactiveTrackEndIcon,
                contents = Contents.Inactive.TrackEndIcon,
                isEnabled = isEnabled,
                colors = colors,
                state = measurePolicy,
                trackMeasurePolicy = measurePolicy,
            )
        },
        modifier = modifier,
@@ -135,7 +135,7 @@ private fun TrackIcon(
    icon: (@Composable BoxScope.(sliderIconsState: SliderIconsState) -> Unit)?,
    isEnabled: Boolean,
    contents: Contents,
    state: SliderIconsState,
    trackMeasurePolicy: TrackMeasurePolicy,
    colors: SliderColors,
    modifier: Modifier = Modifier,
) {
@@ -164,7 +164,11 @@ private fun TrackIcon(
            }
        }
    Box(modifier = modifier.layoutId(contents).fillMaxSize()) {
        CompositionLocalProvider(LocalContentColor provides iconColor) { icon(state) }
        if (trackMeasurePolicy.isVisible(contents) != null) {
            CompositionLocalProvider(LocalContentColor provides iconColor) {
                icon(trackMeasurePolicy)
            }
        }
    }
}

@@ -176,25 +180,27 @@ private class TrackMeasurePolicy(
    private val isVertical: Boolean,
) : MeasurePolicy, SliderIconsState {

    private val isVisible: Map<Contents, MutableState<Boolean>> =
    private val isVisible: Map<Contents, MutableState<Boolean?>> =
        mutableMapOf(
            Contents.Active.TrackStartIcon to mutableStateOf(false),
            Contents.Active.TrackEndIcon to mutableStateOf(false),
            Contents.Inactive.TrackStartIcon to mutableStateOf(false),
            Contents.Inactive.TrackEndIcon to mutableStateOf(false),
            Contents.Active.TrackStartIcon to mutableStateOf(null),
            Contents.Active.TrackEndIcon to mutableStateOf(null),
            Contents.Inactive.TrackStartIcon to mutableStateOf(null),
            Contents.Inactive.TrackEndIcon to mutableStateOf(null),
        )

    fun isVisible(contents: Contents): Boolean? = isVisible.getValue(contents.resolve()).value

    override val isActiveTrackStartIconVisible: Boolean
        get() = isVisible.getValue(Contents.Active.TrackStartIcon.resolve()).value
        get() = isVisible(Contents.Active.TrackStartIcon)!!

    override val isActiveTrackEndIconVisible: Boolean
        get() = isVisible.getValue(Contents.Active.TrackEndIcon.resolve()).value
        get() = isVisible(Contents.Active.TrackEndIcon)!!

    override val isInactiveTrackStartIconVisible: Boolean
        get() = isVisible.getValue(Contents.Inactive.TrackStartIcon.resolve()).value
        get() = isVisible(Contents.Inactive.TrackStartIcon)!!

    override val isInactiveTrackEndIconVisible: Boolean
        get() = isVisible.getValue(Contents.Inactive.TrackEndIcon.resolve()).value
        get() = isVisible(Contents.Inactive.TrackEndIcon)!!

    override fun MeasureScope.measure(
        measurables: List<Measurable>,
@@ -252,8 +258,7 @@ private class TrackMeasurePolicy(

                // isVisible is only relevant for the icons
                if (iconLayoutId != Contents.Track) {
                    val isVisibleState = isVisible.getValue(iconLayoutId)
                    val newIsVisible =
                    val isIconVisible =
                        iconLayoutId.isVisible(
                            placeableDimension =
                                if (isVertical) iconPlaceable.height else iconPlaceable.width,
@@ -261,9 +266,7 @@ private class TrackMeasurePolicy(
                            gapSize = gapSizePx,
                            coercedValueAsFraction = coercedValueAsFraction,
                        )
                    if (isVisibleState.value != newIsVisible) {
                        isVisibleState.value = newIsVisible
                    }
                    isVisible.getValue(iconLayoutId).value = isIconVisible
                }
            }
        }
+26 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.volume.ui.compose.slider

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -24,6 +25,10 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

@@ -33,13 +38,32 @@ fun SliderIcon(
    isVisible: Boolean,
    modifier: Modifier = Modifier,
) {
    var previousIsVisible: Boolean? by remember { mutableStateOf(null) }
    Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxSize()) {
        AnimatedVisibility(
            visible = isVisible,
            enter = fadeIn(animationSpec = tween(delayMillis = 33, durationMillis = 100)),
            exit = fadeOut(animationSpec = tween(durationMillis = 50)),
            enter =
                fadeIn(
                    animationSpec =
                        if (previousIsVisible == null) {
                            snap()
                        } else {
                            tween(delayMillis = 33, durationMillis = 100)
                        }
                ),
            exit =
                fadeOut(
                    animationSpec =
                        if (previousIsVisible == null) {
                            snap()
                        } else {
                            tween(durationMillis = 50)
                        }
                ),
        ) {
            icon()
        }
    }

    previousIsVisible = isVisible
}