diff options
17 files changed, 552 insertions, 210 deletions
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt index f490968b7a7c..8af131d975f1 100644 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene import android.view.View +import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor @@ -62,12 +63,14 @@ interface LockscreenSceneModule { notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory, blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>, clockInteractor: KeyguardClockInteractor, + interactionJankMonitor: InteractionJankMonitor, ): LockscreenContent { return LockscreenContent( viewModelFactory, notificationScrimViewModelFactory, blueprints, clockInteractor, + interactionJankMonitor, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt index 7b2f9dc76158..ba85f9570d09 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.composable +import android.view.View import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -25,8 +26,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.ContentScope +import com.android.internal.jank.Cuj +import com.android.internal.jank.Cuj.CujType +import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallback import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel import com.android.systemui.lifecycle.rememberViewModel @@ -44,6 +50,7 @@ class LockscreenContent( private val notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory, private val blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>, private val clockInteractor: KeyguardClockInteractor, + private val interactionJankMonitor: InteractionJankMonitor, ) { private val blueprintByBlueprintId: Map<String, ComposableLockscreenSceneBlueprint> by lazy { blueprints.associateBy { it.id } @@ -51,8 +58,14 @@ class LockscreenContent( @Composable fun ContentScope.Content(modifier: Modifier = Modifier) { + val view = LocalView.current val viewModel = - rememberViewModel("LockscreenContent-viewModel") { viewModelFactory.create() } + rememberViewModel("LockscreenContent-viewModel") { + viewModelFactory.create( + keyguardTransitionAnimationCallback = + KeyguardTransitionAnimationCallbackImpl(view, interactionJankMonitor) + ) + } val notificationLockscreenScrimViewModel = rememberViewModel("LockscreenContent-scrimViewModel") { notificationScrimViewModelFactory.create() @@ -69,7 +82,6 @@ class LockscreenContent( val coroutineScope = rememberCoroutineScope() val blueprintId by viewModel.blueprintId(coroutineScope).collectAsStateWithLifecycle() - val view = LocalView.current DisposableEffect(view) { clockInteractor.clockEventController.registerListeners(view) @@ -83,3 +95,30 @@ class LockscreenContent( } } } + +private class KeyguardTransitionAnimationCallbackImpl( + private val view: View, + private val interactionJankMonitor: InteractionJankMonitor, +) : KeyguardTransitionAnimationCallback { + + override fun onAnimationStarted(from: KeyguardState, to: KeyguardState) { + cujOrNull(from, to)?.let { cuj -> interactionJankMonitor.begin(view, cuj) } + } + + override fun onAnimationEnded(from: KeyguardState, to: KeyguardState) { + cujOrNull(from, to)?.let { cuj -> interactionJankMonitor.end(cuj) } + } + + override fun onAnimationCanceled(from: KeyguardState, to: KeyguardState) { + cujOrNull(from, to)?.let { cuj -> interactionJankMonitor.cancel(cuj) } + } + + @CujType + private fun cujOrNull(from: KeyguardState, to: KeyguardState): Int? { + return when { + from == KeyguardState.AOD -> Cuj.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD + to == KeyguardState.AOD -> Cuj.CUJ_LOCKSCREEN_TRANSITION_TO_AOD + else -> null + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallbackTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallbackTest.kt new file mode 100644 index 000000000000..6e44bc708e74 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallbackTest.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.shared.transition + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class FakeKeyguardTransitionAnimationCallbackTest : SysuiTestCase() { + + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + + val underTest = FakeKeyguardTransitionAnimationCallback() + + @Test + fun onAnimationStarted() = + kosmos.runTest { + assertThat(underTest.activeAnimations).isEmpty() + + underTest.onAnimationStarted(KeyguardState.LOCKSCREEN, KeyguardState.AOD) + assertThat(underTest.activeAnimations).hasSize(1) + + underTest.onAnimationStarted(KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER) + assertThat(underTest.activeAnimations).hasSize(2) + } + + @Test + fun onAnimationEnded() = + kosmos.runTest { + underTest.onAnimationStarted(KeyguardState.LOCKSCREEN, KeyguardState.AOD) + underTest.onAnimationStarted(KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER) + assertThat(underTest.activeAnimations).hasSize(2) + + underTest.onAnimationEnded(KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER) + assertThat(underTest.activeAnimations).hasSize(1) + + underTest.onAnimationEnded(KeyguardState.LOCKSCREEN, KeyguardState.AOD) + assertThat(underTest.activeAnimations).isEmpty() + } + + @Test + fun onAnimationCanceled() = + kosmos.runTest { + underTest.onAnimationStarted(KeyguardState.LOCKSCREEN, KeyguardState.AOD) + underTest.onAnimationStarted(KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER) + assertThat(underTest.activeAnimations).hasSize(2) + + underTest.onAnimationCanceled(KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER) + assertThat(underTest.activeAnimations).hasSize(1) + + underTest.onAnimationCanceled(KeyguardState.LOCKSCREEN, KeyguardState.AOD) + assertThat(underTest.activeAnimations).isEmpty() + } + + @Test(expected = IllegalStateException::class) + fun onAnimationEnded_throwsWhenNoSuchAnimation() = + kosmos.runTest { + underTest.onAnimationStarted(KeyguardState.LOCKSCREEN, KeyguardState.AOD) + underTest.onAnimationStarted(KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER) + assertThat(underTest.activeAnimations).hasSize(2) + + underTest.onAnimationEnded(KeyguardState.AOD, KeyguardState.LOCKSCREEN) + } + + @Test(expected = IllegalStateException::class) + fun onAnimationCanceled_throwsWhenNoSuchAnimation() = + kosmos.runTest { + underTest.onAnimationStarted(KeyguardState.LOCKSCREEN, KeyguardState.AOD) + underTest.onAnimationStarted(KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER) + assertThat(underTest.activeAnimations).hasSize(2) + + underTest.onAnimationCanceled(KeyguardState.AOD, KeyguardState.LOCKSCREEN) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt index bd0fb68a9c42..af025273458f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt @@ -21,7 +21,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.authController import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository -import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.andSceneContainer import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository @@ -29,7 +28,12 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepos import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.transition.fakeKeyguardTransitionAnimationCallback +import com.android.systemui.keyguard.shared.transition.keyguardTransitionAnimationCallbackDelegator import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runCurrent +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R @@ -42,8 +46,7 @@ import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import java.util.Locale -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.Job import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -56,7 +59,8 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa private val kosmos: Kosmos = testKosmos() - lateinit var underTest: LockscreenContentViewModel + private lateinit var underTest: LockscreenContentViewModel + private val activationJob = Job() companion object { @JvmStatic @@ -74,225 +78,207 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa fun setup() { with(kosmos) { shadeRepository.setShadeLayoutWide(false) - underTest = lockscreenContentViewModel - underTest.activateIn(testScope) + underTest = + lockscreenContentViewModelFactory.create(fakeKeyguardTransitionAnimationCallback) + underTest.activateIn(testScope, activationJob) } } @Test fun isUdfpsVisible_withUdfps_true() = - with(kosmos) { - testScope.runTest { - whenever(authController.isUdfpsSupported).thenReturn(true) - assertThat(underTest.isUdfpsVisible).isTrue() - } + kosmos.runTest { + whenever(authController.isUdfpsSupported).thenReturn(true) + assertThat(underTest.isUdfpsVisible).isTrue() } @Test fun isUdfpsVisible_withoutUdfps_false() = - with(kosmos) { - testScope.runTest { - whenever(authController.isUdfpsSupported).thenReturn(false) - assertThat(underTest.isUdfpsVisible).isFalse() - } + kosmos.runTest { + whenever(authController.isUdfpsSupported).thenReturn(false) + assertThat(underTest.isUdfpsVisible).isFalse() } @Test @DisableSceneContainer fun clockSize_withLargeClock_true() = - with(kosmos) { - testScope.runTest { - val clockSize by collectLastValue(underTest.clockSize) - fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) - assertThat(clockSize).isEqualTo(ClockSize.LARGE) - } + kosmos.runTest { + val clockSize by collectLastValue(underTest.clockSize) + fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) + assertThat(clockSize).isEqualTo(ClockSize.LARGE) } @Test @DisableSceneContainer fun clockSize_withSmallClock_false() = - with(kosmos) { - testScope.runTest { - val clockSize by collectLastValue(underTest.clockSize) - fakeKeyguardClockRepository.setClockSize(ClockSize.SMALL) - assertThat(clockSize).isEqualTo(ClockSize.SMALL) - } + kosmos.runTest { + val clockSize by collectLastValue(underTest.clockSize) + fakeKeyguardClockRepository.setClockSize(ClockSize.SMALL) + assertThat(clockSize).isEqualTo(ClockSize.SMALL) } @Test fun areNotificationsVisible_splitShadeTrue_true() = - with(kosmos) { - testScope.runTest { - val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) - shadeRepository.setShadeLayoutWide(true) - fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) + kosmos.runTest { + val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) + shadeRepository.setShadeLayoutWide(true) + fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) - assertThat(areNotificationsVisible).isTrue() - } + assertThat(areNotificationsVisible).isTrue() } @Test fun areNotificationsVisible_dualShadeWideOnLockscreen_true() = - with(kosmos) { - testScope.runTest { - val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) - kosmos.enableDualShade() - shadeRepository.setShadeLayoutWide(true) - fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) + kosmos.runTest { + val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) + kosmos.enableDualShade() + shadeRepository.setShadeLayoutWide(true) + fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) - assertThat(areNotificationsVisible).isTrue() - } + assertThat(areNotificationsVisible).isTrue() } @Test @DisableSceneContainer fun areNotificationsVisible_withSmallClock_true() = - with(kosmos) { - testScope.runTest { - val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) - fakeKeyguardClockRepository.setClockSize(ClockSize.SMALL) - assertThat(areNotificationsVisible).isTrue() - } + kosmos.runTest { + val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) + fakeKeyguardClockRepository.setClockSize(ClockSize.SMALL) + assertThat(areNotificationsVisible).isTrue() } @Test @DisableSceneContainer fun areNotificationsVisible_withLargeClock_false() = - with(kosmos) { - testScope.runTest { - val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) - fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) - assertThat(areNotificationsVisible).isFalse() - } + kosmos.runTest { + val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) + fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) + assertThat(areNotificationsVisible).isFalse() } @Test fun isShadeLayoutWide_withConfigTrue_true() = - with(kosmos) { - testScope.runTest { - val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) - shadeRepository.setShadeLayoutWide(true) + kosmos.runTest { + val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) + shadeRepository.setShadeLayoutWide(true) - assertThat(isShadeLayoutWide).isTrue() - } + assertThat(isShadeLayoutWide).isTrue() } @Test fun isShadeLayoutWide_withConfigFalse_false() = - with(kosmos) { - testScope.runTest { - val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) - shadeRepository.setShadeLayoutWide(false) + kosmos.runTest { + val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) + shadeRepository.setShadeLayoutWide(false) - assertThat(isShadeLayoutWide).isFalse() - } + assertThat(isShadeLayoutWide).isFalse() } @Test fun unfoldTranslations() = - with(kosmos) { - testScope.runTest { - val maxTranslation = prepareConfiguration() - val translations by collectLastValue(underTest.unfoldTranslations) - - val unfoldProvider = fakeUnfoldTransitionProgressProvider - unfoldProvider.onTransitionStarted() - assertThat(translations?.start).isEqualTo(0f) - assertThat(translations?.end).isEqualTo(-0f) - - repeat(10) { repetition -> - val transitionProgress = 0.1f * (repetition + 1) - unfoldProvider.onTransitionProgress(transitionProgress) - assertThat(translations?.start) - .isEqualTo((1 - transitionProgress) * maxTranslation) - assertThat(translations?.end) - .isEqualTo(-(1 - transitionProgress) * maxTranslation) - } - - unfoldProvider.onTransitionFinishing() - assertThat(translations?.start).isEqualTo(0f) - assertThat(translations?.end).isEqualTo(-0f) - - unfoldProvider.onTransitionFinished() - assertThat(translations?.start).isEqualTo(0f) - assertThat(translations?.end).isEqualTo(-0f) + kosmos.runTest { + val maxTranslation = prepareConfiguration() + val translations by collectLastValue(underTest.unfoldTranslations) + + val unfoldProvider = fakeUnfoldTransitionProgressProvider + unfoldProvider.onTransitionStarted() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) + + repeat(10) { repetition -> + val transitionProgress = 0.1f * (repetition + 1) + unfoldProvider.onTransitionProgress(transitionProgress) + assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation) + assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation) } + + unfoldProvider.onTransitionFinishing() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) + + unfoldProvider.onTransitionFinished() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) } @Test fun isContentVisible_whenNotOccluded_visible() = - with(kosmos) { - testScope.runTest { - val isContentVisible by collectLastValue(underTest.isContentVisible) + kosmos.runTest { + val isContentVisible by collectLastValue(underTest.isContentVisible) - keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, null) - runCurrent() - assertThat(isContentVisible).isTrue() - } + keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, null) + runCurrent() + assertThat(isContentVisible).isTrue() } @Test fun isContentVisible_whenOccluded_notVisible() = - with(kosmos) { - testScope.runTest { - val isContentVisible by collectLastValue(underTest.isContentVisible) - - keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) - fakeKeyguardTransitionRepository.transitionTo( - KeyguardState.LOCKSCREEN, - KeyguardState.OCCLUDED, - ) - runCurrent() - assertThat(isContentVisible).isFalse() - } + kosmos.runTest { + val isContentVisible by collectLastValue(underTest.isContentVisible) + + keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) + fakeKeyguardTransitionRepository.transitionTo( + KeyguardState.LOCKSCREEN, + KeyguardState.OCCLUDED, + ) + runCurrent() + assertThat(isContentVisible).isFalse() } @Test fun isContentVisible_whenOccluded_notVisible_evenIfShadeShown() = - with(kosmos) { - testScope.runTest { - val isContentVisible by collectLastValue(underTest.isContentVisible) - - keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) - fakeKeyguardTransitionRepository.transitionTo( - KeyguardState.LOCKSCREEN, - KeyguardState.OCCLUDED, - ) - runCurrent() - - sceneInteractor.snapToScene(Scenes.Shade, "") - runCurrent() - assertThat(isContentVisible).isFalse() - } + kosmos.runTest { + val isContentVisible by collectLastValue(underTest.isContentVisible) + + keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) + fakeKeyguardTransitionRepository.transitionTo( + KeyguardState.LOCKSCREEN, + KeyguardState.OCCLUDED, + ) + runCurrent() + + sceneInteractor.snapToScene(Scenes.Shade, "") + runCurrent() + assertThat(isContentVisible).isFalse() + } + + @Test + fun activate_setsDelegate_onKeyguardTransitionAnimationCallbackDelegator() = + kosmos.runTest { + runCurrent() + assertThat(keyguardTransitionAnimationCallbackDelegator.delegate) + .isSameInstanceAs(fakeKeyguardTransitionAnimationCallback) + } + + @Test + fun deactivate_clearsDelegate_onKeyguardTransitionAnimationCallbackDelegator() = + kosmos.runTest { + activationJob.cancel() + runCurrent() + assertThat(keyguardTransitionAnimationCallbackDelegator.delegate).isNull() } @Test fun isContentVisible_whenOccluded_notVisibleInOccluded_visibleInAod() = - with(kosmos) { - testScope.runTest { - val isContentVisible by collectLastValue(underTest.isContentVisible) - keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) - fakeKeyguardTransitionRepository.transitionTo( - KeyguardState.LOCKSCREEN, - KeyguardState.OCCLUDED, - ) - runCurrent() - - sceneInteractor.snapToScene(Scenes.Shade, "") - runCurrent() - assertThat(isContentVisible).isFalse() - - fakeKeyguardTransitionRepository.transitionTo( - KeyguardState.OCCLUDED, - KeyguardState.AOD, - ) - runCurrent() - - sceneInteractor.snapToScene(Scenes.Lockscreen, "") - runCurrent() - - assertThat(isContentVisible).isTrue() - } + kosmos.runTest { + val isContentVisible by collectLastValue(underTest.isContentVisible) + keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) + fakeKeyguardTransitionRepository.transitionTo( + KeyguardState.LOCKSCREEN, + KeyguardState.OCCLUDED, + ) + runCurrent() + + sceneInteractor.snapToScene(Scenes.Shade, "") + runCurrent() + assertThat(isContentVisible).isFalse() + + fakeKeyguardTransitionRepository.transitionTo(KeyguardState.OCCLUDED, KeyguardState.AOD) + runCurrent() + + sceneInteractor.snapToScene(Scenes.Lockscreen, "") + runCurrent() + + assertThat(isContentVisible).isTrue() } private fun prepareConfiguration(): Int { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/dagger/BouncerLoggerModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/dagger/BouncerLoggerModule.kt index 8b253077a3dd..ab2ff2f84e65 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/dagger/BouncerLoggerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/dagger/BouncerLoggerModule.kt @@ -17,6 +17,7 @@ package com.android.systemui.bouncer.dagger import com.android.systemui.CoreStartable +import com.android.systemui.bouncer.domain.interactor.BouncerMessageAuditLogger import com.android.systemui.bouncer.log.BouncerLoggerStartable import dagger.Binds import dagger.Module @@ -30,4 +31,9 @@ interface BouncerLoggerModule { @IntoMap @ClassKey(BouncerLoggerStartable::class) fun bindBouncerLoggerStartable(impl: BouncerLoggerStartable): CoreStartable + + @Binds + @IntoMap + @ClassKey(BouncerMessageAuditLogger::class) + fun bind(impl: BouncerMessageAuditLogger): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt index 355ce6a3798f..64d33b2648c2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt @@ -17,13 +17,13 @@ package com.android.systemui.bouncer.domain.interactor import android.util.Log +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.bouncer.data.repository.BouncerMessageRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import com.android.app.tracing.coroutines.launchTraced as launch private val TAG = BouncerMessageAuditLogger::class.simpleName!! diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 905bbe226bb7..908413db22dd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -61,6 +61,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; +import com.android.systemui.keyguard.ui.binder.SideFpsProgressBarViewBinder; import com.android.systemui.keyguard.ui.transitions.BlurConfig; import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule; import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewBinder; @@ -271,4 +272,10 @@ public interface KeyguardModule { @IntoMap @ClassKey(AlternateBouncerWindowViewBinder.class) CoreStartable bindsAlternateBouncerWindowViewBinder(AlternateBouncerWindowViewBinder binder); + + /** A silly lint rule made me write this, this is a self-documenting function! */ + @Binds + @IntoMap + @ClassKey(SideFpsProgressBarViewBinder.class) + CoreStartable bindSideFpsProgressBarViewBinder(SideFpsProgressBarViewBinder impl); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index 3956901aaa88..126b375efb7d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -16,33 +16,30 @@ package com.android.systemui.keyguard.data.repository -import com.android.systemui.CoreStartable import com.android.systemui.bouncer.data.repository.BouncerMessageRepository import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl -import com.android.systemui.bouncer.domain.interactor.BouncerMessageAuditLogger -import com.android.systemui.keyguard.ui.binder.SideFpsProgressBarViewBinder +import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallback +import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallbackDelegator import dagger.Binds import dagger.Module -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap @Module interface KeyguardRepositoryModule { @Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository @Binds - @IntoMap - @ClassKey(SideFpsProgressBarViewBinder::class) - fun bindSideFpsProgressBarViewBinder(viewBinder: SideFpsProgressBarViewBinder): CoreStartable - - @Binds fun keyguardSurfaceBehindRepository( impl: KeyguardSurfaceBehindRepositoryImpl ): KeyguardSurfaceBehindRepository @Binds + fun keyguardTransitionAnimationCallback( + impl: KeyguardTransitionAnimationCallbackDelegator + ): KeyguardTransitionAnimationCallback + + @Binds fun keyguardTransitionRepository( impl: KeyguardTransitionRepositoryImpl ): KeyguardTransitionRepository @@ -68,11 +65,6 @@ interface KeyguardRepositoryModule { @Binds fun bouncerMessageRepository(impl: BouncerMessageRepositoryImpl): BouncerMessageRepository - @Binds - @IntoMap - @ClassKey(BouncerMessageAuditLogger::class) - fun bind(impl: BouncerMessageAuditLogger): CoreStartable - @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository @Binds fun keyguardClockRepository(impl: KeyguardClockRepositoryImpl): KeyguardClockRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 24f2493c626d..2a1cb12c153e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallback import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -131,7 +132,10 @@ interface KeyguardTransitionRepository { @SysUISingleton class KeyguardTransitionRepositoryImpl @Inject -constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionRepository { +constructor( + @Main private val mainDispatcher: CoroutineDispatcher, + private val transitionCallback: KeyguardTransitionAnimationCallback, +) : KeyguardTransitionRepository { /** * Each transition between [KeyguardState]s will have an associated Flow. In order to collect * these events, clients should call [transition]. @@ -252,16 +256,19 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR animatorListener = object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) { + transitionCallback.onAnimationStarted(info.from, info.to) emitTransition( TransitionStep(info, startingValue, TransitionState.STARTED) ) } override fun onAnimationCancel(animation: Animator) { + transitionCallback.onAnimationCanceled(info.from, info.to) endAnimation(lastStep.value, TransitionState.CANCELED) } override fun onAnimationEnd(animation: Animator) { + transitionCallback.onAnimationEnded(info.from, info.to) endAnimation(1f, TransitionState.FINISHED) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallback.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallback.kt new file mode 100644 index 000000000000..6ecc5d3abea3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallback.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.keyguard.shared.transition + +import com.android.systemui.keyguard.shared.model.KeyguardState + +/** + * Defines an interface for classes that can be notified when a keyguard transition starts, ends, or + * is canceled. + */ +interface KeyguardTransitionAnimationCallback { + + /** Notifies that an animation from [from] to [to] has started. */ + fun onAnimationStarted(from: KeyguardState, to: KeyguardState) + + /** Notifies that an animation from [from] to [to] has ended. */ + fun onAnimationEnded(from: KeyguardState, to: KeyguardState) + + /** Notifies that an animation from [from] to [to] has been canceled. */ + fun onAnimationCanceled(from: KeyguardState, to: KeyguardState) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallbackDelegator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallbackDelegator.kt new file mode 100644 index 000000000000..5de9e326c0c2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallbackDelegator.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.keyguard.shared.transition + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.KeyguardState +import javax.inject.Inject + +@SysUISingleton +class KeyguardTransitionAnimationCallbackDelegator @Inject constructor() : + KeyguardTransitionAnimationCallback { + + var delegate: KeyguardTransitionAnimationCallback? = null + + override fun onAnimationStarted(from: KeyguardState, to: KeyguardState) { + delegate?.onAnimationStarted(from, to) + } + + override fun onAnimationEnded(from: KeyguardState, to: KeyguardState) { + delegate?.onAnimationEnded(from, to) + } + + override fun onAnimationCanceled(from: KeyguardState, to: KeyguardState) { + delegate?.onAnimationCanceled(from, to) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt index b6a3b6aaba14..3e3a89a55f66 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt @@ -27,10 +27,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallback +import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallbackDelegator import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor +import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope @@ -57,6 +60,9 @@ constructor( private val unfoldTransitionInteractor: UnfoldTransitionInteractor, private val deviceEntryInteractor: DeviceEntryInteractor, private val transitionInteractor: KeyguardTransitionInteractor, + private val keyguardTransitionAnimationCallbackDelegator: + KeyguardTransitionAnimationCallbackDelegator, + @Assisted private val keyguardTransitionAnimationCallback: KeyguardTransitionAnimationCallback, ) : ExclusiveActivatable() { @VisibleForTesting val clockSize = clockInteractor.clockSize @@ -79,29 +85,36 @@ constructor( override suspend fun onActivated(): Nothing { coroutineScope { - launch { - combine( - unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = true), - unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false), - ) { start, end -> - UnfoldTranslations(start = start, end = end) - } - .collect { _unfoldTranslations.value = it } + try { + keyguardTransitionAnimationCallbackDelegator.delegate = + keyguardTransitionAnimationCallback + launch { + combine( + unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = true), + unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false), + ) { start, end -> + UnfoldTranslations(start = start, end = end) + } + .collect { _unfoldTranslations.value = it } + } + + launch { + transitionInteractor + .transitionValue(KeyguardState.OCCLUDED) + .map { it > 0f } + .collect { fullyOrPartiallyOccluded -> + // Content is visible unless we're OCCLUDED. Currently, we don't have + // nice + // animations into and out of OCCLUDED, so the lockscreen/AOD content is + // hidden immediately upon entering/exiting OCCLUDED. + _isContentVisible.value = !fullyOrPartiallyOccluded + } + } + + awaitCancellation() + } finally { + keyguardTransitionAnimationCallbackDelegator.delegate = null } - - launch { - transitionInteractor - .transitionValue(KeyguardState.OCCLUDED) - .map { it > 0f } - .collect { fullyOrPartiallyOccluded -> - // Content is visible unless we're OCCLUDED. Currently, we don't have nice - // animations into and out of OCCLUDED, so the lockscreen/AOD content is - // hidden immediately upon entering/exiting OCCLUDED. - _isContentVisible.value = !fullyOrPartiallyOccluded - } - } - - awaitCancellation() } } @@ -151,6 +164,8 @@ constructor( @AssistedFactory interface Factory { - fun create(): LockscreenContentViewModel + fun create( + keyguardTransitionAnimationCallback: KeyguardTransitionAnimationCallback + ): LockscreenContentViewModel } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index 77c40a1e8eef..f14a9f5a1885 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.keyguard.shared.transition.keyguardTransitionAnimationCallback import com.android.systemui.keyguard.util.FrameCallbackProvider import com.android.systemui.keyguard.util.KeyguardTransitionRunner import com.android.systemui.kosmos.testDispatcher @@ -81,7 +82,11 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { @Before fun setUp() { - underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main) + underTest = + KeyguardTransitionRepositoryImpl( + Dispatchers.Main, + kosmos.keyguardTransitionAnimationCallback, + ) runBlocking { callbackProvider = FrameCallbackProvider(testScope.backgroundScope) withContext(Dispatchers.Main) { @@ -411,7 +416,11 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { @Test fun simulateRaceConditionIsProcessedInOrder() = testScope.runTest { - val ktr = KeyguardTransitionRepositoryImpl(kosmos.testDispatcher) + val ktr = + KeyguardTransitionRepositoryImpl( + kosmos.testDispatcher, + kosmos.keyguardTransitionAnimationCallback, + ) val steps by collectValues(ktr.transitions.dropWhile { step -> step.from == OFF }) // Add a delay to the first transition in order to attempt to have the second transition @@ -448,7 +457,11 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { @Test fun simulateRaceConditionIsProcessedInOrderUsingUpdateTransition() = testScope.runTest { - val ktr = KeyguardTransitionRepositoryImpl(kosmos.testDispatcher) + val ktr = + KeyguardTransitionRepositoryImpl( + kosmos.testDispatcher, + kosmos.keyguardTransitionAnimationCallback, + ) val steps by collectValues(ktr.transitions.dropWhile { step -> step.from == OFF }) // Begin a manual transition diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt index e9eea83d54df..dacc78a35092 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.data.repository +import com.android.systemui.keyguard.shared.transition.keyguardTransitionAnimationCallbackDelegator import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope @@ -28,4 +29,9 @@ var Kosmos.fakeKeyguardTransitionRepository by var Kosmos.fakeKeyguardTransitionRepositorySpy: FakeKeyguardTransitionRepository by Kosmos.Fixture { spy(fakeKeyguardTransitionRepository) } var Kosmos.realKeyguardTransitionRepository: KeyguardTransitionRepository by - Kosmos.Fixture { KeyguardTransitionRepositoryImpl(testDispatcher) } + Kosmos.Fixture { + KeyguardTransitionRepositoryImpl( + testDispatcher, + keyguardTransitionAnimationCallbackDelegator, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallback.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallback.kt new file mode 100644 index 000000000000..6a1ced52c7c9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallback.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.shared.transition + +import com.android.systemui.keyguard.shared.model.KeyguardState + +class FakeKeyguardTransitionAnimationCallback : KeyguardTransitionAnimationCallback { + + private val _activeAnimations = mutableListOf<TransitionAnimation>() + /** The animations that have been started but not yet ended nor canceled. */ + val activeAnimations: List<TransitionAnimation> + get() = _activeAnimations.toList() + + private val _finishedAnimations = mutableListOf<TransitionAnimation>() + /** The animations that have ended. */ + val finishedAnimations: List<TransitionAnimation> + get() = _finishedAnimations.toList() + + private val _canceledAnimations = mutableListOf<TransitionAnimation>() + /** The animations that have been canceled. */ + val canceledAnimations: List<TransitionAnimation> + get() = _canceledAnimations.toList() + + override fun onAnimationStarted(from: KeyguardState, to: KeyguardState) { + _activeAnimations.add(TransitionAnimation(from = from, to = to)) + } + + override fun onAnimationEnded(from: KeyguardState, to: KeyguardState) { + val animation = TransitionAnimation(from = from, to = to) + check(_activeAnimations.remove(animation)) { "Ending an animation that wasn't started!" } + _finishedAnimations.add(animation) + } + + override fun onAnimationCanceled(from: KeyguardState, to: KeyguardState) { + val animation = TransitionAnimation(from = from, to = to) + check(_activeAnimations.remove(animation)) { "Canceling an animation that wasn't started!" } + _canceledAnimations.add(animation) + } + + data class TransitionAnimation(val from: KeyguardState, val to: KeyguardState) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallbackKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallbackKosmos.kt new file mode 100644 index 000000000000..a445d2c17cd3 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/shared/transition/KeyguardTransitionAnimationCallbackKosmos.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.shared.transition + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +val Kosmos.keyguardTransitionAnimationCallbackDelegator by Fixture { + KeyguardTransitionAnimationCallbackDelegator() +} + +val Kosmos.keyguardTransitionAnimationCallback: KeyguardTransitionAnimationCallback by Fixture { + fakeKeyguardTransitionAnimationCallback +} + +val Kosmos.fakeKeyguardTransitionAnimationCallback by Fixture { + FakeKeyguardTransitionAnimationCallback() +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt index 78d44d4917fe..dd13b8b143ae 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt @@ -21,20 +21,31 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.keyguard.shared.transition.KeyguardTransitionAnimationCallback +import com.android.systemui.keyguard.shared.transition.keyguardTransitionAnimationCallbackDelegator import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor -val Kosmos.lockscreenContentViewModel by - Kosmos.Fixture { - LockscreenContentViewModel( - clockInteractor = keyguardClockInteractor, - interactor = keyguardBlueprintInteractor, - authController = authController, - touchHandling = keyguardTouchHandlingViewModel, - shadeInteractor = shadeInteractor, - unfoldTransitionInteractor = unfoldTransitionInteractor, - deviceEntryInteractor = deviceEntryInteractor, - transitionInteractor = keyguardTransitionInteractor, - ) +val Kosmos.lockscreenContentViewModelFactory by Fixture { + object : LockscreenContentViewModel.Factory { + override fun create( + keyguardTransitionAnimationCallback: KeyguardTransitionAnimationCallback + ): LockscreenContentViewModel { + return LockscreenContentViewModel( + clockInteractor = keyguardClockInteractor, + interactor = keyguardBlueprintInteractor, + authController = authController, + touchHandling = keyguardTouchHandlingViewModel, + shadeInteractor = shadeInteractor, + unfoldTransitionInteractor = unfoldTransitionInteractor, + deviceEntryInteractor = deviceEntryInteractor, + transitionInteractor = keyguardTransitionInteractor, + keyguardTransitionAnimationCallbackDelegator = + keyguardTransitionAnimationCallbackDelegator, + keyguardTransitionAnimationCallback = keyguardTransitionAnimationCallback, + ) + } } +} |