Loading packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, ) } } Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +41 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 } Loading @@ -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() Loading @@ -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) Loading @@ -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 } } } packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallbackTest.kt 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * 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) } } packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +138 −152 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -74,67 +78,57 @@ 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 { kosmos.runTest { whenever(authController.isUdfpsSupported).thenReturn(true) assertThat(underTest.isUdfpsVisible).isTrue() } } @Test fun isUdfpsVisible_withoutUdfps_false() = with(kosmos) { testScope.runTest { kosmos.runTest { whenever(authController.isUdfpsSupported).thenReturn(false) assertThat(underTest.isUdfpsVisible).isFalse() } } @Test @DisableSceneContainer fun clockSize_withLargeClock_true() = with(kosmos) { testScope.runTest { 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 { 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 { kosmos.runTest { val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) shadeRepository.setShadeLayoutWide(true) fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) assertThat(areNotificationsVisible).isTrue() } } @Test fun areNotificationsVisible_dualShadeWideOnLockscreen_true() = with(kosmos) { testScope.runTest { kosmos.runTest { val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) kosmos.enableDualShade() shadeRepository.setShadeLayoutWide(true) Loading @@ -142,56 +136,46 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa assertThat(areNotificationsVisible).isTrue() } } @Test @DisableSceneContainer fun areNotificationsVisible_withSmallClock_true() = with(kosmos) { testScope.runTest { 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 { kosmos.runTest { val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) assertThat(areNotificationsVisible).isFalse() } } @Test fun isShadeLayoutWide_withConfigTrue_true() = with(kosmos) { testScope.runTest { kosmos.runTest { val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) shadeRepository.setShadeLayoutWide(true) assertThat(isShadeLayoutWide).isTrue() } } @Test fun isShadeLayoutWide_withConfigFalse_false() = with(kosmos) { testScope.runTest { kosmos.runTest { val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) shadeRepository.setShadeLayoutWide(false) assertThat(isShadeLayoutWide).isFalse() } } @Test fun unfoldTranslations() = with(kosmos) { testScope.runTest { kosmos.runTest { val maxTranslation = prepareConfiguration() val translations by collectLastValue(underTest.unfoldTranslations) Loading @@ -203,10 +187,8 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa 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) assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation) assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation) } unfoldProvider.onTransitionFinishing() Loading @@ -217,24 +199,20 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) } } @Test fun isContentVisible_whenNotOccluded_visible() = with(kosmos) { testScope.runTest { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, null) runCurrent() assertThat(isContentVisible).isTrue() } } @Test fun isContentVisible_whenOccluded_notVisible() = with(kosmos) { testScope.runTest { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) Loading @@ -245,12 +223,10 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa runCurrent() assertThat(isContentVisible).isFalse() } } @Test fun isContentVisible_whenOccluded_notVisible_evenIfShadeShown() = with(kosmos) { testScope.runTest { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) Loading @@ -264,12 +240,26 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa 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 { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) fakeKeyguardTransitionRepository.transitionTo( Loading @@ -282,10 +272,7 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa runCurrent() assertThat(isContentVisible).isFalse() fakeKeyguardTransitionRepository.transitionTo( KeyguardState.OCCLUDED, KeyguardState.AOD, ) fakeKeyguardTransitionRepository.transitionTo(KeyguardState.OCCLUDED, KeyguardState.AOD) runCurrent() sceneInteractor.snapToScene(Scenes.Lockscreen, "") Loading @@ -293,7 +280,6 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa assertThat(isContentVisible).isTrue() } } private fun prepareConfiguration(): Int { val configuration = context.resources.configuration Loading packages/SystemUI/src/com/android/systemui/bouncer/dagger/BouncerLoggerModule.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 } Loading
packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, ) } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +41 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 } Loading @@ -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() Loading @@ -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) Loading @@ -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 } } }
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/shared/transition/FakeKeyguardTransitionAnimationCallbackTest.kt 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * 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) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +138 −152 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -74,67 +78,57 @@ 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 { kosmos.runTest { whenever(authController.isUdfpsSupported).thenReturn(true) assertThat(underTest.isUdfpsVisible).isTrue() } } @Test fun isUdfpsVisible_withoutUdfps_false() = with(kosmos) { testScope.runTest { kosmos.runTest { whenever(authController.isUdfpsSupported).thenReturn(false) assertThat(underTest.isUdfpsVisible).isFalse() } } @Test @DisableSceneContainer fun clockSize_withLargeClock_true() = with(kosmos) { testScope.runTest { 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 { 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 { kosmos.runTest { val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) shadeRepository.setShadeLayoutWide(true) fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) assertThat(areNotificationsVisible).isTrue() } } @Test fun areNotificationsVisible_dualShadeWideOnLockscreen_true() = with(kosmos) { testScope.runTest { kosmos.runTest { val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) kosmos.enableDualShade() shadeRepository.setShadeLayoutWide(true) Loading @@ -142,56 +136,46 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa assertThat(areNotificationsVisible).isTrue() } } @Test @DisableSceneContainer fun areNotificationsVisible_withSmallClock_true() = with(kosmos) { testScope.runTest { 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 { kosmos.runTest { val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible()) fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE) assertThat(areNotificationsVisible).isFalse() } } @Test fun isShadeLayoutWide_withConfigTrue_true() = with(kosmos) { testScope.runTest { kosmos.runTest { val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) shadeRepository.setShadeLayoutWide(true) assertThat(isShadeLayoutWide).isTrue() } } @Test fun isShadeLayoutWide_withConfigFalse_false() = with(kosmos) { testScope.runTest { kosmos.runTest { val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) shadeRepository.setShadeLayoutWide(false) assertThat(isShadeLayoutWide).isFalse() } } @Test fun unfoldTranslations() = with(kosmos) { testScope.runTest { kosmos.runTest { val maxTranslation = prepareConfiguration() val translations by collectLastValue(underTest.unfoldTranslations) Loading @@ -203,10 +187,8 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa 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) assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation) assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation) } unfoldProvider.onTransitionFinishing() Loading @@ -217,24 +199,20 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) } } @Test fun isContentVisible_whenNotOccluded_visible() = with(kosmos) { testScope.runTest { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, null) runCurrent() assertThat(isContentVisible).isTrue() } } @Test fun isContentVisible_whenOccluded_notVisible() = with(kosmos) { testScope.runTest { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) Loading @@ -245,12 +223,10 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa runCurrent() assertThat(isContentVisible).isFalse() } } @Test fun isContentVisible_whenOccluded_notVisible_evenIfShadeShown() = with(kosmos) { testScope.runTest { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) Loading @@ -264,12 +240,26 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa 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 { kosmos.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) fakeKeyguardTransitionRepository.transitionTo( Loading @@ -282,10 +272,7 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa runCurrent() assertThat(isContentVisible).isFalse() fakeKeyguardTransitionRepository.transitionTo( KeyguardState.OCCLUDED, KeyguardState.AOD, ) fakeKeyguardTransitionRepository.transitionTo(KeyguardState.OCCLUDED, KeyguardState.AOD) runCurrent() sceneInteractor.snapToScene(Scenes.Lockscreen, "") Loading @@ -293,7 +280,6 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa assertThat(isContentVisible).isTrue() } } private fun prepareConfiguration(): Int { val configuration = context.resources.configuration Loading
packages/SystemUI/src/com/android/systemui/bouncer/dagger/BouncerLoggerModule.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 }