Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1784,3 +1784,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "keyguard_transition_force_finish_on_screen_off" namespace: "systemui" description: "Forces KTF transitions to finish if the screen turns all the way off." bug: "331636736" metadata { purpose: PURPOSE_BUGFIX } } No newline at end of file packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +58 −6 Original line number Diff line number Diff line Loading @@ -16,11 +16,14 @@ package com.android.systemui.keyguard.data.repository import android.animation.Animator import android.animation.ValueAnimator import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.shared.model.KeyguardState Loading @@ -41,6 +44,8 @@ import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import java.math.RoundingMode import java.util.UUID import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.dropWhile Loading @@ -53,6 +58,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -65,6 +71,8 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { private lateinit var underTest: KeyguardTransitionRepository private lateinit var runner: KeyguardTransitionRunner private val animatorListener = mock<Animator.AnimatorListener>() @Before fun setUp() { underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main) Loading @@ -80,7 +88,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { runner.startTransition( this, TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), maxFrames = 100 maxFrames = 100, ) assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN) Loading @@ -107,7 +115,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), TransitionModeOnCanceled.LAST_VALUE TransitionModeOnCanceled.LAST_VALUE, ), ) Loading Loading @@ -142,7 +150,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), TransitionModeOnCanceled.RESET TransitionModeOnCanceled.RESET, ), ) Loading Loading @@ -177,7 +185,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), TransitionModeOnCanceled.REVERSE TransitionModeOnCanceled.REVERSE, ), ) Loading Loading @@ -476,6 +484,49 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { assertThat(steps.size).isEqualTo(3) } @Test @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF) fun forceFinishCurrentTransition_noFurtherStepsEmitted() = testScope.runTest { val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF }) var sentForceFinish = false runner.startTransition( this, TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), maxFrames = 100, // Force-finish on the second frame. frameCallback = { frameNumber -> if (!sentForceFinish && frameNumber > 1) { testScope.launch { underTest.forceFinishCurrentTransition() } sentForceFinish = true } }, ) val lastTwoRunningSteps = steps.filter { it.transitionState == TransitionState.RUNNING }.takeLast(2) // Make sure we stopped emitting RUNNING steps early, but then emitted a final 1f step. assertTrue(lastTwoRunningSteps[0].value < 0.5f) assertTrue(lastTwoRunningSteps[1].value == 1f) assertEquals(steps.last().from, AOD) assertEquals(steps.last().to, LOCKSCREEN) assertEquals(steps.last().transitionState, TransitionState.FINISHED) } @Test @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF) fun forceFinishCurrentTransition_noTransitionStarted_noStepsEmitted() = testScope.runTest { val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF }) underTest.forceFinishCurrentTransition() assertEquals(0, steps.size) } private fun listWithStep( step: BigDecimal, start: BigDecimal = BigDecimal.ZERO, Loading Loading @@ -505,7 +556,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { to, fractions[0].toFloat(), TransitionState.STARTED, OWNER_NAME OWNER_NAME, ) ) fractions.forEachIndexed { index, fraction -> Loading @@ -519,7 +570,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { to, fraction.toFloat(), TransitionState.RUNNING, OWNER_NAME OWNER_NAME, ) ) } Loading @@ -538,6 +589,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) setDuration(10) addListener(animatorListener) } } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt +19 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.animation.ValueAnimator import android.view.Choreographer.FrameCallback import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.TransitionInfo import java.util.function.Consumer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job Loading @@ -35,9 +36,8 @@ import org.junit.Assert.fail * Gives direct control over ValueAnimator, in order to make transition tests deterministic. See * [AnimationHandler]. Animators are required to be run on the main thread, so dispatch accordingly. */ class KeyguardTransitionRunner( val repository: KeyguardTransitionRepository, ) : AnimationFrameCallbackProvider { class KeyguardTransitionRunner(val repository: KeyguardTransitionRepository) : AnimationFrameCallbackProvider { private var frameCount = 1L private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null)) Loading @@ -48,7 +48,12 @@ class KeyguardTransitionRunner( * For transitions being directed by an animator. Will control the number of frames being * generated so the values are deterministic. */ suspend fun startTransition(scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100) { suspend fun startTransition( scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100, frameCallback: Consumer<Long>? = null, ) { // AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from main // thread withContext(Dispatchers.Main) { Loading @@ -62,7 +67,12 @@ class KeyguardTransitionRunner( isTerminated = frameNumber >= maxFrames if (!isTerminated) { try { withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) } frameCallback?.accept(frameNumber) } catch (e: IllegalStateException) { e.printStackTrace() } } } } Loading Loading @@ -90,9 +100,13 @@ class KeyguardTransitionRunner( override fun postFrameCallback(cb: FrameCallback) { frames.value = Pair(frameCount++, cb) } override fun postCommitCallback(runnable: Runnable) {} override fun getFrameTime() = frameCount override fun getFrameDelay() = 1L override fun setFrameDelay(delay: Long) {} companion object { Loading packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +43 −2 Original line number Diff line number Diff line Loading @@ -114,6 +114,18 @@ interface KeyguardTransitionRepository { @FloatRange(from = 0.0, to = 1.0) value: Float, state: TransitionState, ) /** * Forces the current transition to emit FINISHED, foregoing any additional RUNNING steps that * otherwise would have been emitted. * * When the screen is off, upcoming performance changes cause all Animators to cease emitting * frames, which means the Animator passed to [startTransition] will never finish if it was * running when the screen turned off. Also, there's simply no reason to emit RUNNING steps when * the screen isn't even on. As long as we emit FINISHED, everything should end up in the * correct state. */ suspend fun forceFinishCurrentTransition() } @SysUISingleton Loading @@ -134,6 +146,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR override val transitions = _transitions.asSharedFlow().distinctUntilChanged() private var lastStep: TransitionStep = TransitionStep() private var lastAnimator: ValueAnimator? = null private var animatorListener: AnimatorListenerAdapter? = null private val withContextMutex = Mutex() private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> = Loading Loading @@ -233,7 +246,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR ) } val adapter = animatorListener = object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) { emitTransition( Loading @@ -254,9 +267,10 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR animator.removeListener(this) animator.removeUpdateListener(updateListener) lastAnimator = null animatorListener = null } } animator.addListener(adapter) animator.addListener(animatorListener) animator.addUpdateListener(updateListener) animator.start() return@withContext null Loading Loading @@ -290,6 +304,33 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR } } override suspend fun forceFinishCurrentTransition() { withContextMutex.lock() if (lastAnimator?.isRunning != true) { return } return withContext("$TAG#forceFinishCurrentTransition", mainDispatcher) { withContextMutex.unlock() Log.d(TAG, "forceFinishCurrentTransition() - emitting FINISHED early.") lastAnimator?.apply { // Cancel the animator, but remove listeners first so we don't emit CANCELED. removeAllListeners() cancel() // Emit a final 1f RUNNING step to ensure that any transitions not listening for a // FINISHED step end up in the right end state. emitTransition(TransitionStep(currentTransitionInfo, 1f, TransitionState.RUNNING)) // Ask the listener to emit FINISHED and clean up its state. animatorListener?.onAnimationEnd(this) } } } private suspend fun updateTransitionInternal( transitionId: UUID, @FloatRange(from = 0.0, to = 1.0) value: Float, Loading packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +17 −1 Original line number Diff line number Diff line Loading @@ -19,8 +19,10 @@ package com.android.systemui.keyguard.domain.interactor import android.annotation.SuppressLint import android.util.Log import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags.keyguardTransitionForceFinishOnScreenOff import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository Loading @@ -30,6 +32,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes Loading Loading @@ -59,7 +63,6 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates business-logic related to the keyguard transitions. */ @OptIn(ExperimentalCoroutinesApi::class) Loading @@ -70,6 +73,7 @@ constructor( @Application val scope: CoroutineScope, private val repository: KeyguardTransitionRepository, private val sceneInteractor: SceneInteractor, private val powerInteractor: PowerInteractor, ) { private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>() Loading Loading @@ -188,6 +192,18 @@ constructor( } } } if (keyguardTransitionForceFinishOnScreenOff()) { /** * If the screen is turning off, finish the current transition immediately. Further * frames won't be visible anyway. */ scope.launch { powerInteractor.screenPowerState .filter { it == ScreenPowerState.SCREEN_TURNING_OFF } .collect { repository.forceFinishCurrentTransition() } } } } fun transition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<TransitionStep> { Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -1784,3 +1784,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "keyguard_transition_force_finish_on_screen_off" namespace: "systemui" description: "Forces KTF transitions to finish if the screen turns all the way off." bug: "331636736" metadata { purpose: PURPOSE_BUGFIX } } No newline at end of file
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +58 −6 Original line number Diff line number Diff line Loading @@ -16,11 +16,14 @@ package com.android.systemui.keyguard.data.repository import android.animation.Animator import android.animation.ValueAnimator import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.shared.model.KeyguardState Loading @@ -41,6 +44,8 @@ import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import java.math.RoundingMode import java.util.UUID import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.dropWhile Loading @@ -53,6 +58,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -65,6 +71,8 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { private lateinit var underTest: KeyguardTransitionRepository private lateinit var runner: KeyguardTransitionRunner private val animatorListener = mock<Animator.AnimatorListener>() @Before fun setUp() { underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main) Loading @@ -80,7 +88,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { runner.startTransition( this, TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), maxFrames = 100 maxFrames = 100, ) assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN) Loading @@ -107,7 +115,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), TransitionModeOnCanceled.LAST_VALUE TransitionModeOnCanceled.LAST_VALUE, ), ) Loading Loading @@ -142,7 +150,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), TransitionModeOnCanceled.RESET TransitionModeOnCanceled.RESET, ), ) Loading Loading @@ -177,7 +185,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), TransitionModeOnCanceled.REVERSE TransitionModeOnCanceled.REVERSE, ), ) Loading Loading @@ -476,6 +484,49 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { assertThat(steps.size).isEqualTo(3) } @Test @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF) fun forceFinishCurrentTransition_noFurtherStepsEmitted() = testScope.runTest { val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF }) var sentForceFinish = false runner.startTransition( this, TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), maxFrames = 100, // Force-finish on the second frame. frameCallback = { frameNumber -> if (!sentForceFinish && frameNumber > 1) { testScope.launch { underTest.forceFinishCurrentTransition() } sentForceFinish = true } }, ) val lastTwoRunningSteps = steps.filter { it.transitionState == TransitionState.RUNNING }.takeLast(2) // Make sure we stopped emitting RUNNING steps early, but then emitted a final 1f step. assertTrue(lastTwoRunningSteps[0].value < 0.5f) assertTrue(lastTwoRunningSteps[1].value == 1f) assertEquals(steps.last().from, AOD) assertEquals(steps.last().to, LOCKSCREEN) assertEquals(steps.last().transitionState, TransitionState.FINISHED) } @Test @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF) fun forceFinishCurrentTransition_noTransitionStarted_noStepsEmitted() = testScope.runTest { val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF }) underTest.forceFinishCurrentTransition() assertEquals(0, steps.size) } private fun listWithStep( step: BigDecimal, start: BigDecimal = BigDecimal.ZERO, Loading Loading @@ -505,7 +556,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { to, fractions[0].toFloat(), TransitionState.STARTED, OWNER_NAME OWNER_NAME, ) ) fractions.forEachIndexed { index, fraction -> Loading @@ -519,7 +570,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { to, fraction.toFloat(), TransitionState.RUNNING, OWNER_NAME OWNER_NAME, ) ) } Loading @@ -538,6 +589,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) setDuration(10) addListener(animatorListener) } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt +19 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.animation.ValueAnimator import android.view.Choreographer.FrameCallback import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.TransitionInfo import java.util.function.Consumer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job Loading @@ -35,9 +36,8 @@ import org.junit.Assert.fail * Gives direct control over ValueAnimator, in order to make transition tests deterministic. See * [AnimationHandler]. Animators are required to be run on the main thread, so dispatch accordingly. */ class KeyguardTransitionRunner( val repository: KeyguardTransitionRepository, ) : AnimationFrameCallbackProvider { class KeyguardTransitionRunner(val repository: KeyguardTransitionRepository) : AnimationFrameCallbackProvider { private var frameCount = 1L private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null)) Loading @@ -48,7 +48,12 @@ class KeyguardTransitionRunner( * For transitions being directed by an animator. Will control the number of frames being * generated so the values are deterministic. */ suspend fun startTransition(scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100) { suspend fun startTransition( scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100, frameCallback: Consumer<Long>? = null, ) { // AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from main // thread withContext(Dispatchers.Main) { Loading @@ -62,7 +67,12 @@ class KeyguardTransitionRunner( isTerminated = frameNumber >= maxFrames if (!isTerminated) { try { withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) } frameCallback?.accept(frameNumber) } catch (e: IllegalStateException) { e.printStackTrace() } } } } Loading Loading @@ -90,9 +100,13 @@ class KeyguardTransitionRunner( override fun postFrameCallback(cb: FrameCallback) { frames.value = Pair(frameCount++, cb) } override fun postCommitCallback(runnable: Runnable) {} override fun getFrameTime() = frameCount override fun getFrameDelay() = 1L override fun setFrameDelay(delay: Long) {} companion object { Loading
packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +43 −2 Original line number Diff line number Diff line Loading @@ -114,6 +114,18 @@ interface KeyguardTransitionRepository { @FloatRange(from = 0.0, to = 1.0) value: Float, state: TransitionState, ) /** * Forces the current transition to emit FINISHED, foregoing any additional RUNNING steps that * otherwise would have been emitted. * * When the screen is off, upcoming performance changes cause all Animators to cease emitting * frames, which means the Animator passed to [startTransition] will never finish if it was * running when the screen turned off. Also, there's simply no reason to emit RUNNING steps when * the screen isn't even on. As long as we emit FINISHED, everything should end up in the * correct state. */ suspend fun forceFinishCurrentTransition() } @SysUISingleton Loading @@ -134,6 +146,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR override val transitions = _transitions.asSharedFlow().distinctUntilChanged() private var lastStep: TransitionStep = TransitionStep() private var lastAnimator: ValueAnimator? = null private var animatorListener: AnimatorListenerAdapter? = null private val withContextMutex = Mutex() private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> = Loading Loading @@ -233,7 +246,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR ) } val adapter = animatorListener = object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) { emitTransition( Loading @@ -254,9 +267,10 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR animator.removeListener(this) animator.removeUpdateListener(updateListener) lastAnimator = null animatorListener = null } } animator.addListener(adapter) animator.addListener(animatorListener) animator.addUpdateListener(updateListener) animator.start() return@withContext null Loading Loading @@ -290,6 +304,33 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR } } override suspend fun forceFinishCurrentTransition() { withContextMutex.lock() if (lastAnimator?.isRunning != true) { return } return withContext("$TAG#forceFinishCurrentTransition", mainDispatcher) { withContextMutex.unlock() Log.d(TAG, "forceFinishCurrentTransition() - emitting FINISHED early.") lastAnimator?.apply { // Cancel the animator, but remove listeners first so we don't emit CANCELED. removeAllListeners() cancel() // Emit a final 1f RUNNING step to ensure that any transitions not listening for a // FINISHED step end up in the right end state. emitTransition(TransitionStep(currentTransitionInfo, 1f, TransitionState.RUNNING)) // Ask the listener to emit FINISHED and clean up its state. animatorListener?.onAnimationEnd(this) } } } private suspend fun updateTransitionInternal( transitionId: UUID, @FloatRange(from = 0.0, to = 1.0) value: Float, Loading
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +17 −1 Original line number Diff line number Diff line Loading @@ -19,8 +19,10 @@ package com.android.systemui.keyguard.domain.interactor import android.annotation.SuppressLint import android.util.Log import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags.keyguardTransitionForceFinishOnScreenOff import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository Loading @@ -30,6 +32,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes Loading Loading @@ -59,7 +63,6 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates business-logic related to the keyguard transitions. */ @OptIn(ExperimentalCoroutinesApi::class) Loading @@ -70,6 +73,7 @@ constructor( @Application val scope: CoroutineScope, private val repository: KeyguardTransitionRepository, private val sceneInteractor: SceneInteractor, private val powerInteractor: PowerInteractor, ) { private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>() Loading Loading @@ -188,6 +192,18 @@ constructor( } } } if (keyguardTransitionForceFinishOnScreenOff()) { /** * If the screen is turning off, finish the current transition immediately. Further * frames won't be visible anyway. */ scope.launch { powerInteractor.screenPowerState .filter { it == ScreenPowerState.SCREEN_TURNING_OFF } .collect { repository.forceFinishCurrentTransition() } } } } fun transition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<TransitionStep> { Loading