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

Commit fbc94240 authored by Anton Potapov's avatar Anton Potapov Committed by Android (Google) Code Review
Browse files

Merge "Remove slider animation when set model first time" into main

parents 99af61e1 6d8cbd52
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -570,6 +570,8 @@
        <item name="trackCornerSize">12dp</item>
        <item name="trackInsideCornerSize">2dp</item>
        <item name="trackStopIndicatorSize">6dp</item>
        <item name="trackIconSize">20dp</item>
        <item name="labelBehavior">gone</item>
    </style>

    <style name="SystemUI.Material3.Slider" parent="@style/Widget.Material3.Slider">
@@ -579,6 +581,7 @@
        <item name="tickColorInactive">@androidprv:color/materialColorPrimary</item>
        <item name="trackColorActive">@androidprv:color/materialColorPrimary</item>
        <item name="trackColorInactive">@androidprv:color/materialColorSurfaceContainerHighest</item>
        <item name="trackIconActiveColor">@androidprv:color/materialColorSurfaceContainerHighest</item>
    </style>

    <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
+42 −37
Original line number Diff line number Diff line
@@ -16,18 +16,15 @@

package com.android.systemui.volume.dialog.sliders.ui

import android.animation.Animator
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.view.View
import android.view.animation.DecelerateInterpolator
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
import javax.inject.Inject
import kotlin.math.roundToInt
@@ -35,53 +32,61 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L

@VolumeDialogSliderScope
class VolumeDialogSliderViewBinder
@Inject
constructor(
    private val viewModel: VolumeDialogSliderViewModel,
    private val jankListenerFactory: JankListenerFactory,
) {
constructor(private val viewModel: VolumeDialogSliderViewModel) {

    fun CoroutineScope.bind(view: View) {
        val sliderView: Slider =
            view.requireViewById<Slider>(R.id.volume_dialog_slider).apply {
                labelBehavior = LabelFormatter.LABEL_GONE
                trackIconActiveColor = trackInactiveTintList
    private val sliderValueProperty =
        object : FloatPropertyCompat<Slider>("value") {
            override fun getValue(slider: Slider): Float = slider.value

            override fun setValue(slider: Slider, value: Float) {
                slider.value = value
            }
        }
    private val springForce =
        SpringForce().apply {
            stiffness = SpringForce.STIFFNESS_MEDIUM
            dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
        }

    fun CoroutineScope.bind(view: View) {
        var isInitialUpdate = true
        val sliderView: Slider = view.requireViewById(R.id.volume_dialog_slider)
        val animation = SpringAnimation(sliderView, sliderValueProperty)
        animation.spring = springForce

        sliderView.addOnChangeListener { _, value, fromUser ->
            viewModel.setStreamVolume(value.roundToInt(), fromUser)
        }

        viewModel.state.onEach { sliderView.setModel(it) }.launchIn(this)
        viewModel.state
            .onEach {
                sliderView.setModel(it, animation, isInitialUpdate)
                isInitialUpdate = false
            }
            .launchIn(this)
    }

    @SuppressLint("UseCompatLoadingForDrawables")
    private suspend fun Slider.setModel(model: VolumeDialogSliderStateModel) {
    private fun Slider.setModel(
        model: VolumeDialogSliderStateModel,
        animation: SpringAnimation,
        isInitialUpdate: Boolean,
    ) {
        valueFrom = model.minValue
        animation.setMinValue(model.minValue)
        valueTo = model.maxValue
        animation.setMaxValue(model.maxValue)
        // coerce the current value to the new value range before animating it. This prevents
        // animating from the value that is outside of current [valueFrom, valueTo].
        value = value.coerceIn(valueFrom, valueTo)
        setValueAnimated(
            model.value,
            jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
        )
        trackIconActiveEnd = context.getDrawable(model.iconRes)
        setTrackIconActiveStart(model.iconRes)
        if (isInitialUpdate) {
            value = model.value
        } else {
            animation.animateToFinalPosition(model.value)
        }
    }

private suspend fun Slider.setValueAnimated(
    newValue: Float,
    jankListener: Animator.AnimatorListener,
) {
    ObjectAnimator.ofFloat(value, newValue)
        .apply {
            duration = PROGRESS_CHANGE_ANIMATION_DURATION_MS
            interpolator = DecelerateInterpolator()
            addListener(jankListener)
        }
        .suspendAnimate<Float> { value = it }
}
+16 −13
Original line number Diff line number Diff line
@@ -73,7 +73,8 @@ constructor(
            .filterNotNull()

    val state: Flow<VolumeDialogSliderStateModel> =
        model.flatMapLatest { streamModel ->
        model
            .flatMapLatest { streamModel ->
                with(streamModel) {
                        volumeDialogSliderIconProvider.getStreamIcon(
                            stream = stream,
@@ -86,6 +87,8 @@ constructor(
                    }
                    .map { icon -> streamModel.toStateModel(icon) }
            }
            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
            .filterNotNull()

    init {
        userVolumeUpdates
+16 −7
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.volume.dialog.ui.utils
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.view.ViewPropertyAnimator
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringAnimation
@@ -69,17 +70,25 @@ suspend fun ViewPropertyAnimator.suspendAnimate(
@Suppress("UNCHECKED_CAST")
suspend fun <T> ValueAnimator.suspendAnimate(onValueChanged: (T) -> Unit) {
    suspendCancellableCoroutine { continuation ->
        addListener(
        val listener =
            object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) = continuation.resumeIfCan(Unit)
                    override fun onAnimationEnd(animation: Animator) =
                        continuation.resumeIfCan(Unit)

                override fun onAnimationCancel(animation: Animator) = continuation.resumeIfCan(Unit)
                    override fun onAnimationCancel(animation: Animator) =
                        continuation.resumeIfCan(Unit)
                }
        )
        addUpdateListener { onValueChanged(it.animatedValue as T) }
                .also(::addListener)
        val updateListener =
            AnimatorUpdateListener { onValueChanged(it.animatedValue as T) }
                .also(::addUpdateListener)

        start()
        continuation.invokeOnCancellation { cancel() }
        continuation.invokeOnCancellation {
            removeUpdateListener(updateListener)
            removeListener(listener)
            cancel()
        }
    }
}