Loading packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt +33 −41 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.widget.ImageButton import androidx.annotation.LayoutRes import androidx.compose.ui.util.fastForEachIndexed import androidx.constraintlayout.motion.widget.MotionLayout import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatValueHolder import androidx.dynamicanimation.animation.SpringAnimation import androidx.dynamicanimation.animation.SpringForce Loading @@ -44,13 +43,16 @@ import com.android.systemui.volume.dialog.ui.utils.suspendAnimate import javax.inject.Inject import kotlin.properties.Delegates import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.launch private const val CLOSE_DRAWER_DELAY = 300L @OptIn(ExperimentalCoroutinesApi::class) @VolumeDialogScope class VolumeDialogRingerViewBinder @Inject Loading Loading @@ -95,7 +97,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate() viewModel.ringerViewModel .onEach { ringerState -> .mapLatest { ringerState -> when (ringerState) { is RingerViewModelState.Available -> { val uiModel = ringerState.uiModel Loading Loading @@ -221,15 +223,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { val unselectedButton = getChildAt(count - previousIndex - 1) .requireViewById<ImageButton>(R.id.volume_drawer_button) // On roundness animation end. val roundnessAnimationEndListener = DynamicAnimation.OnAnimationEndListener { _, _, _, _ -> postDelayed( { bindButtons(viewModel, uiModel, onAnimationEnd, isAnimated = true) }, CLOSE_DRAWER_DELAY, ) } // We only need to execute on roundness animation end and volume dialog background // progress update once because these changes should be applied once on volume dialog // background and ringer drawer views. Loading @@ -242,7 +235,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { } else { { _, _ -> } }, roundnessAnimationEndListener, ) } val unselectedCornerRadius = Loading @@ -257,6 +249,12 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { }, ) } coroutineScope { launch { delay(CLOSE_DRAWER_DELAY) bindButtons(viewModel, uiModel, onAnimationEnd, isAnimated = true) } } } else { bindButtons(viewModel, uiModel, onAnimationEnd) } Loading Loading @@ -348,7 +346,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { private suspend fun ImageButton.animateTo( ringerButtonUiModel: RingerButtonUiModel, onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> }, roundnessAnimationEndListener: DynamicAnimation.OnAnimationEndListener? = null, ) { val roundnessAnimation = SpringAnimation(FloatValueHolder(0F)).setSpring(roundnessSpringForce) Loading @@ -356,14 +353,9 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { val radius = (background as GradientDrawable).cornerRadius val cornerRadiusDiff = ringerButtonUiModel.cornerRadius - (background as GradientDrawable).cornerRadius val roundnessAnimationUpdateListener = DynamicAnimation.OnAnimationUpdateListener { _, value, _ -> onProgressChanged(value, cornerRadiusDiff > 0F) (background as GradientDrawable).cornerRadius = radius + value * cornerRadiusDiff background.invalidateSelf() } val colorAnimationUpdateListener = DynamicAnimation.OnAnimationUpdateListener { _, value, _ -> coroutineScope { launch { colorAnimation.suspendAnimate { value -> val currentIconColor = rgbEvaluator.evaluate( value.coerceIn(0F, 1F), Loading @@ -381,12 +373,12 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { background.invalidateSelf() setColorFilter(currentIconColor) } coroutineScope { launch { colorAnimation.suspendAnimate(colorAnimationUpdateListener) } roundnessAnimation.suspendAnimate( roundnessAnimationUpdateListener, roundnessAnimationEndListener, ) } roundnessAnimation.suspendAnimate { value -> onProgressChanged(value, cornerRadiusDiff > 0F) (background as GradientDrawable).cornerRadius = radius + value * cornerRadiusDiff background.invalidateSelf() } } } Loading packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt +14 −15 Original line number Diff line number Diff line Loading @@ -87,19 +87,18 @@ suspend fun <T> ValueAnimator.suspendAnimate(onValueChanged: (T) -> Unit) { * Starts spring animation and suspends until it's finished. Cancels the animation if the running * coroutine is cancelled. */ suspend fun SpringAnimation.suspendAnimate( animationUpdateListener: DynamicAnimation.OnAnimationUpdateListener? = null, animationEndListener: DynamicAnimation.OnAnimationEndListener? = null, ) = suspendCancellableCoroutine { continuation -> animationUpdateListener?.let(::addUpdateListener) addEndListener { animation, canceled, value, velocity -> continuation.resumeIfCan(Unit) animationEndListener?.onAnimationEnd(animation, canceled, value, velocity) } suspend fun SpringAnimation.suspendAnimate(onAnimationUpdate: (Float) -> Unit) = suspendCancellableCoroutine { continuation -> val updateListener = DynamicAnimation.OnAnimationUpdateListener { _, value, _ -> onAnimationUpdate(value) } val endListener = DynamicAnimation.OnAnimationEndListener { _, _, _, _ -> continuation.resumeIfCan(Unit) } addUpdateListener(updateListener) addEndListener(endListener) animateToFinalPosition(1F) continuation.invokeOnCancellation { animationUpdateListener?.let(::removeUpdateListener) animationEndListener?.let(::removeEndListener) removeUpdateListener(updateListener) removeEndListener(endListener) cancel() } } Loading Loading
packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt +33 −41 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.widget.ImageButton import androidx.annotation.LayoutRes import androidx.compose.ui.util.fastForEachIndexed import androidx.constraintlayout.motion.widget.MotionLayout import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatValueHolder import androidx.dynamicanimation.animation.SpringAnimation import androidx.dynamicanimation.animation.SpringForce Loading @@ -44,13 +43,16 @@ import com.android.systemui.volume.dialog.ui.utils.suspendAnimate import javax.inject.Inject import kotlin.properties.Delegates import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.launch private const val CLOSE_DRAWER_DELAY = 300L @OptIn(ExperimentalCoroutinesApi::class) @VolumeDialogScope class VolumeDialogRingerViewBinder @Inject Loading Loading @@ -95,7 +97,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate() viewModel.ringerViewModel .onEach { ringerState -> .mapLatest { ringerState -> when (ringerState) { is RingerViewModelState.Available -> { val uiModel = ringerState.uiModel Loading Loading @@ -221,15 +223,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { val unselectedButton = getChildAt(count - previousIndex - 1) .requireViewById<ImageButton>(R.id.volume_drawer_button) // On roundness animation end. val roundnessAnimationEndListener = DynamicAnimation.OnAnimationEndListener { _, _, _, _ -> postDelayed( { bindButtons(viewModel, uiModel, onAnimationEnd, isAnimated = true) }, CLOSE_DRAWER_DELAY, ) } // We only need to execute on roundness animation end and volume dialog background // progress update once because these changes should be applied once on volume dialog // background and ringer drawer views. Loading @@ -242,7 +235,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { } else { { _, _ -> } }, roundnessAnimationEndListener, ) } val unselectedCornerRadius = Loading @@ -257,6 +249,12 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { }, ) } coroutineScope { launch { delay(CLOSE_DRAWER_DELAY) bindButtons(viewModel, uiModel, onAnimationEnd, isAnimated = true) } } } else { bindButtons(viewModel, uiModel, onAnimationEnd) } Loading Loading @@ -348,7 +346,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { private suspend fun ImageButton.animateTo( ringerButtonUiModel: RingerButtonUiModel, onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> }, roundnessAnimationEndListener: DynamicAnimation.OnAnimationEndListener? = null, ) { val roundnessAnimation = SpringAnimation(FloatValueHolder(0F)).setSpring(roundnessSpringForce) Loading @@ -356,14 +353,9 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { val radius = (background as GradientDrawable).cornerRadius val cornerRadiusDiff = ringerButtonUiModel.cornerRadius - (background as GradientDrawable).cornerRadius val roundnessAnimationUpdateListener = DynamicAnimation.OnAnimationUpdateListener { _, value, _ -> onProgressChanged(value, cornerRadiusDiff > 0F) (background as GradientDrawable).cornerRadius = radius + value * cornerRadiusDiff background.invalidateSelf() } val colorAnimationUpdateListener = DynamicAnimation.OnAnimationUpdateListener { _, value, _ -> coroutineScope { launch { colorAnimation.suspendAnimate { value -> val currentIconColor = rgbEvaluator.evaluate( value.coerceIn(0F, 1F), Loading @@ -381,12 +373,12 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { background.invalidateSelf() setColorFilter(currentIconColor) } coroutineScope { launch { colorAnimation.suspendAnimate(colorAnimationUpdateListener) } roundnessAnimation.suspendAnimate( roundnessAnimationUpdateListener, roundnessAnimationEndListener, ) } roundnessAnimation.suspendAnimate { value -> onProgressChanged(value, cornerRadiusDiff > 0F) (background as GradientDrawable).cornerRadius = radius + value * cornerRadiusDiff background.invalidateSelf() } } } Loading
packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt +14 −15 Original line number Diff line number Diff line Loading @@ -87,19 +87,18 @@ suspend fun <T> ValueAnimator.suspendAnimate(onValueChanged: (T) -> Unit) { * Starts spring animation and suspends until it's finished. Cancels the animation if the running * coroutine is cancelled. */ suspend fun SpringAnimation.suspendAnimate( animationUpdateListener: DynamicAnimation.OnAnimationUpdateListener? = null, animationEndListener: DynamicAnimation.OnAnimationEndListener? = null, ) = suspendCancellableCoroutine { continuation -> animationUpdateListener?.let(::addUpdateListener) addEndListener { animation, canceled, value, velocity -> continuation.resumeIfCan(Unit) animationEndListener?.onAnimationEnd(animation, canceled, value, velocity) } suspend fun SpringAnimation.suspendAnimate(onAnimationUpdate: (Float) -> Unit) = suspendCancellableCoroutine { continuation -> val updateListener = DynamicAnimation.OnAnimationUpdateListener { _, value, _ -> onAnimationUpdate(value) } val endListener = DynamicAnimation.OnAnimationEndListener { _, _, _, _ -> continuation.resumeIfCan(Unit) } addUpdateListener(updateListener) addEndListener(endListener) animateToFinalPosition(1F) continuation.invokeOnCancellation { animationUpdateListener?.let(::removeUpdateListener) animationEndListener?.let(::removeEndListener) removeUpdateListener(updateListener) removeEndListener(endListener) cancel() } } Loading