diff options
67 files changed, 3414 insertions, 615 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 92eb8f8c36c2..4950b96b077f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -45,32 +45,32 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.LockscreenShadeTransitionController -import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.testKosmos import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -102,6 +102,7 @@ private const val SENSOR_HEIGHT = 60 @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class UdfpsControllerOverlayTest : SysuiTestCase() { + private val kosmos = testKosmos() @JvmField @Rule var rule = MockitoJUnit.rule() @@ -148,28 +149,11 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Before fun setup() { - testScope = TestScope(StandardTestDispatcher()) - powerRepository = FakePowerRepository() - powerInteractor = - PowerInteractor( - powerRepository, - mock(FalsingCollector::class.java), - mock(ScreenOffAnimationController::class.java), - statusBarStateController, - ) - keyguardTransitionRepository = FakeKeyguardTransitionRepository() - keyguardTransitionInteractor = - KeyguardTransitionInteractor( - scope = testScope.backgroundScope, - repository = keyguardTransitionRepository, - fromLockscreenTransitionInteractor = { - mock(FromLockscreenTransitionInteractor::class.java) - }, - fromPrimaryBouncerTransitionInteractor = { - mock(FromPrimaryBouncerTransitionInteractor::class.java) - }, - fromAodTransitionInteractor = { mock(FromAodTransitionInteractor::class.java) }, - ) + testScope = kosmos.testScope + powerRepository = kosmos.fakePowerRepository + powerInteractor = kosmos.powerInteractor + keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository + keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView) whenever(inflater.inflate(R.layout.udfps_bp_view, null)) .thenReturn(mock(UdfpsBpView::class.java)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt index fb46ed9d54ed..b3380ff6409c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt @@ -25,18 +25,17 @@ import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockSource -import com.android.systemui.power.data.repository.FakePowerRepository -import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest -import com.android.systemui.power.domain.interactor.PowerInteractorFactory +import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LightRevealEffect +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch -import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -49,9 +48,10 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class LightRevealScrimRepositoryTest : SysuiTestCase() { - private lateinit var fakeKeyguardRepository: FakeKeyguardRepository - private lateinit var powerRepository: FakePowerRepository - private lateinit var powerInteractor: PowerInteractor + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val fakeKeyguardRepository = kosmos.fakeKeyguardRepository + private val powerInteractor = kosmos.powerInteractor private lateinit var underTest: LightRevealScrimRepositoryImpl @get:Rule val animatorTestRule = AnimatorTestRule(this) @@ -59,13 +59,13 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - fakeKeyguardRepository = FakeKeyguardRepository() - powerRepository = FakePowerRepository() - powerInteractor = - PowerInteractorFactory.create(repository = powerRepository).powerInteractor - underTest = - LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context, powerInteractor, mock()) + LightRevealScrimRepositoryImpl( + kosmos.fakeKeyguardRepository, + context, + kosmos.powerInteractor, + mock() + ) } @Test @@ -168,8 +168,9 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) fun revealAmount_emitsTo1AfterAnimationStarted() = - runTest(UnconfinedTestDispatcher()) { + testScope.runTest { val value by collectLastValue(underTest.revealAmount) + runCurrent() underTest.startRevealAmountAnimator(true) assertEquals(0.0f, value) animatorTestRule.advanceTimeBy(500L) @@ -179,8 +180,9 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) fun revealAmount_startingRevealTwiceWontRerunAnimator() = - runTest(UnconfinedTestDispatcher()) { + testScope.runTest { val value by collectLastValue(underTest.revealAmount) + runCurrent() underTest.startRevealAmountAnimator(true) assertEquals(0.0f, value) animatorTestRule.advanceTimeBy(250L) @@ -193,12 +195,14 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) fun revealAmount_emitsTo0AfterAnimationStartedReversed() = - runTest(UnconfinedTestDispatcher()) { - val value by collectLastValue(underTest.revealAmount) + testScope.runTest { + val lastValue by collectLastValue(underTest.revealAmount) + runCurrent() + underTest.startRevealAmountAnimator(true) + animatorTestRule.advanceTimeBy(500L) underTest.startRevealAmountAnimator(false) - assertEquals(1.0f, value) animatorTestRule.advanceTimeBy(500L) - assertEquals(0.0f, value) + assertEquals(0.0f, lastValue) } /** diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index f9ec3d161bb0..24c651f3c702 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -18,9 +18,11 @@ package com.android.systemui.keyguard.domain.interactor import android.app.StatusBarManager +import android.platform.test.annotations.DisableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState +import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository @@ -120,6 +122,7 @@ class KeyguardInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testKeyguardGuardVisibilityStopsSecureCamera() = testScope.runTest { val secureCameraActive = collectLastValue(underTest.isSecureCameraActive) @@ -144,6 +147,7 @@ class KeyguardInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testBouncerShowingResetsSecureCameraState() = testScope.runTest { val secureCameraActive = collectLastValue(underTest.isSecureCameraActive) @@ -166,6 +170,7 @@ class KeyguardInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest { val isVisible = collectLastValue(underTest.isKeyguardVisible) repository.setKeyguardShowing(true) diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt index 79455ebb624c..289dbd9f66e0 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt @@ -24,9 +24,12 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.shared.model.BiometricMessage import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage +import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.plugins.ActivityStarter import com.android.systemui.power.domain.interactor.PowerInteractor @@ -60,17 +63,23 @@ constructor( private val context: Context, activityStarter: ActivityStarter, powerInteractor: PowerInteractor, + keyguardTransitionInteractor: KeyguardTransitionInteractor, ) { private val keyguardOccludedByApp: Flow<Boolean> = - combine( - keyguardInteractor.isKeyguardOccluded, - keyguardInteractor.isKeyguardShowing, - primaryBouncerInteractor.isShowing, - alternateBouncerInteractor.isVisible, - ) { occluded, showing, primaryBouncerShowing, alternateBouncerVisible -> - occluded && showing && !primaryBouncerShowing && !alternateBouncerVisible - } - .distinctUntilChanged() + if (KeyguardWmStateRefactor.isEnabled) { + keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.OCCLUDED } + } else { + combine( + keyguardInteractor.isKeyguardOccluded, + keyguardInteractor.isKeyguardShowing, + primaryBouncerInteractor.isShowing, + alternateBouncerInteractor.isVisible, + ) { occluded, showing, primaryBouncerShowing, alternateBouncerVisible -> + occluded && showing && !primaryBouncerShowing && !alternateBouncerVisible + } + .distinctUntilChanged() + } + private val fingerprintUnlockSuccessEvents: Flow<Unit> = fingerprintAuthRepository.authenticationStatus .ifKeyguardOccludedByApp() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 2e233d8e5dd2..3134e35a92e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -289,6 +289,8 @@ public class KeyguardService extends Service { }; } + private final WindowManagerOcclusionManager mWmOcclusionManager; + @Inject public KeyguardService( KeyguardViewMediator keyguardViewMediator, @@ -302,7 +304,8 @@ public class KeyguardService extends Service { KeyguardSurfaceBehindParamsApplier keyguardSurfaceBehindAnimator, @Application CoroutineScope scope, FeatureFlags featureFlags, - PowerInteractor powerInteractor) { + PowerInteractor powerInteractor, + WindowManagerOcclusionManager windowManagerOcclusionManager) { super(); mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; @@ -323,6 +326,8 @@ public class KeyguardService extends Service { keyguardSurfaceBehindAnimator, scope); } + + mWmOcclusionManager = windowManagerOcclusionManager; } @Override @@ -414,7 +419,11 @@ public class KeyguardService extends Service { Trace.beginSection("KeyguardService.mBinder#setOccluded"); checkPermission(); - mKeyguardViewMediator.setOccluded(isOccluded, animate); + if (!KeyguardWmStateRefactor.isEnabled()) { + mKeyguardViewMediator.setOccluded(isOccluded, animate); + } else { + mWmOcclusionManager.onKeyguardServiceSetOccluded(isOccluded); + } Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 6d917bbde82b..a37397db81f8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1365,7 +1365,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager; + private WindowManagerOcclusionManager mWmOcclusionManager; /** + * Injected constructor. See {@link KeyguardModule}. */ public KeyguardViewMediator( @@ -1411,7 +1413,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, SystemPropertiesHelper systemPropertiesHelper, Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager, SelectedUserInteractor selectedUserInteractor, - KeyguardInteractor keyguardInteractor) { + KeyguardInteractor keyguardInteractor, + WindowManagerOcclusionManager wmOcclusionManager) { mContext = context; mUserTracker = userTracker; mFalsingCollector = falsingCollector; @@ -1486,6 +1489,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); mShowKeyguardWakeLock.setReferenceCounted(false); + + mWmOcclusionManager = wmOcclusionManager; } public void userActivity() { @@ -2103,15 +2108,27 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } public IRemoteAnimationRunner getOccludeAnimationRunner() { - return validatingRemoteAnimationRunner(mOccludeAnimationRunner); + if (KeyguardWmStateRefactor.isEnabled()) { + return validatingRemoteAnimationRunner(mWmOcclusionManager.getOccludeAnimationRunner()); + } else { + return validatingRemoteAnimationRunner(mOccludeAnimationRunner); + } } + /** + * TODO(b/326464548): Move this to WindowManagerOcclusionManager + */ public IRemoteAnimationRunner getOccludeByDreamAnimationRunner() { return validatingRemoteAnimationRunner(mOccludeByDreamAnimationRunner); } public IRemoteAnimationRunner getUnoccludeAnimationRunner() { - return validatingRemoteAnimationRunner(mUnoccludeAnimationRunner); + if (KeyguardWmStateRefactor.isEnabled()) { + return validatingRemoteAnimationRunner( + mWmOcclusionManager.getUnoccludeAnimationRunner()); + } else { + return validatingRemoteAnimationRunner(mUnoccludeAnimationRunner); + } } public boolean isHiding() { @@ -2145,8 +2162,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (mOccluded != isOccluded) { mOccluded = isOccluded; - mKeyguardViewControllerLazy.get().setOccluded(isOccluded, animate - && mDeviceInteractive); + if (!KeyguardWmStateRefactor.isEnabled()) { + mKeyguardViewControllerLazy.get().setOccluded(isOccluded, animate + && mDeviceInteractive); + } adjustStatusBarLocked(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt index 8ebcece940c2..00f50023b263 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt @@ -140,8 +140,14 @@ constructor( nonApps: Array<RemoteAnimationTarget>, finishedCallback: IRemoteAnimationFinishedCallback ) { - goingAwayRemoteAnimationFinishedCallback = finishedCallback - keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0]) + if (apps.isNotEmpty()) { + goingAwayRemoteAnimationFinishedCallback = finishedCallback + keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0]) + } else { + // Nothing to do here if we have no apps, end the animation, which will cancel it and WM + // will make *something* visible. + finishedCallback.onAnimationFinished() + } } fun onKeyguardGoingAwayRemoteAnimationCancelled() { @@ -174,13 +180,19 @@ constructor( * if so, true should be the right choice. */ private fun setWmLockscreenState( - lockscreenShowing: Boolean = this.isLockscreenShowing ?: true.also { - Log.d(TAG, "Using isLockscreenShowing=true default in setWmLockscreenState, " + - "because setAodVisible was called before the first setLockscreenShown " + - "call during boot. This is not typical, but is theoretically possible. " + - "If you're investigating the lockscreen showing unexpectedly, start here.") - }, - aodVisible: Boolean = this.isAodVisible + lockscreenShowing: Boolean = + this.isLockscreenShowing + ?: true.also { + Log.d( + TAG, + "Using isLockscreenShowing=true default in setWmLockscreenState, " + + "because setAodVisible was called before the first " + + "setLockscreenShown call during boot. This is not typical, but is " + + "theoretically possible. If you're investigating the lockscreen " + + "showing unexpectedly, start here." + ) + }, + aodVisible: Boolean = this.isAodVisible ) { Log.d( TAG, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt new file mode 100644 index 000000000000..aab90c378a19 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerOcclusionManager.kt @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2024 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 + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Matrix +import android.os.RemoteException +import android.util.Log +import android.view.IRemoteAnimationFinishedCallback +import android.view.IRemoteAnimationRunner +import android.view.RemoteAnimationTarget +import android.view.SyncRtSurfaceTransactionApplier +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED +import androidx.annotation.VisibleForTesting +import com.android.app.animation.Interpolators +import com.android.internal.jank.InteractionJankMonitor +import com.android.internal.policy.ScreenDecorationsUtils +import com.android.keyguard.KeyguardViewController +import com.android.systemui.animation.ActivityTransitionAnimator +import com.android.systemui.animation.TransitionAnimator +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.res.R +import java.util.concurrent.Executor +import javax.inject.Inject + +private val UNOCCLUDE_ANIMATION_DURATION = 250 +private val UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT = 0.1f + +/** + * Keeps track of Window Manager's occlusion state and RemoteAnimations related to changes in + * occlusion state. Occlusion is when a [FLAG_SHOW_WHEN_LOCKED] activity is displaying over the + * lockscreen - we're still locked, but the user can interact with the activity. + * + * Typical "occlusion" use cases include launching the camera over the lockscreen, tapping a quick + * affordance to bring up Google Pay/Wallet/whatever it's called by the time you're reading this, + * and Maps Navigation. + * + * Window Manager considers the keyguard to be 'occluded' whenever a [FLAG_SHOW_WHEN_LOCKED] + * activity is on top of the task stack, even if the device is unlocked and the keyguard is not + * visible. System UI considers the keyguard to be [KeyguardState.OCCLUDED] only when we're on the + * keyguard and an activity is displaying over it. + * + * For all System UI use cases, you should use [KeyguardTransitionInteractor] to determine if we're + * in the [KeyguardState.OCCLUDED] state and react accordingly. If you are sure that you need to + * check whether Window Manager considers OCCLUDED=true even though the lockscreen is not showing, + * use [KeyguardShowWhenLockedActivityInteractor.isShowWhenLockedActivityOnTop] in combination with + * [KeyguardTransitionInteractor] state. + * + * This is a very sensitive piece of state that has caused many headaches in the past. Please be + * careful. + */ +@SysUISingleton +class WindowManagerOcclusionManager +@Inject +constructor( + val keyguardOcclusionInteractor: KeyguardOcclusionInteractor, + val activityTransitionAnimator: ActivityTransitionAnimator, + val keyguardViewController: dagger.Lazy<KeyguardViewController>, + val powerInteractor: PowerInteractor, + val context: Context, + val interactionJankMonitor: InteractionJankMonitor, + @Main executor: Executor, + val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, + val occlusionInteractor: KeyguardOcclusionInteractor, +) { + val powerButtonY = + context.resources.getDimensionPixelSize( + R.dimen.physical_power_button_center_screen_location_y + ) + val windowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context) + + var occludeAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null + + /** + * Animation runner provided to WindowManager, which will be used if an occluding activity is + * launched and Window Manager wants us to animate it in. This is used as a signal that we are + * now occluded, and should update our state accordingly. + */ + val occludeAnimationRunner: IRemoteAnimationRunner = + object : IRemoteAnimationRunner.Stub() { + override fun onAnimationStart( + transit: Int, + apps: Array<RemoteAnimationTarget>, + wallpapers: Array<RemoteAnimationTarget>, + nonApps: Array<RemoteAnimationTarget>, + finishedCallback: IRemoteAnimationFinishedCallback? + ) { + Log.d(TAG, "occludeAnimationRunner#onAnimationStart") + // Wrap the callback so that it's guaranteed to be nulled out once called. + occludeAnimationFinishedCallback = + object : IRemoteAnimationFinishedCallback.Stub() { + override fun onAnimationFinished() { + finishedCallback?.onAnimationFinished() + occludeAnimationFinishedCallback = null + } + } + keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop( + showWhenLockedActivityOnTop = true, + taskInfo = apps.firstOrNull()?.taskInfo, + ) + activityTransitionAnimator + .createRunner(occludeAnimationController) + .onAnimationStart( + transit, + apps, + wallpapers, + nonApps, + occludeAnimationFinishedCallback, + ) + } + + override fun onAnimationCancelled() { + Log.d(TAG, "occludeAnimationRunner#onAnimationCancelled") + } + } + + var unoccludeAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null + + /** + * Animation runner provided to WindowManager, which will be used if an occluding activity is + * finished and Window Manager wants us to animate it out. This is used as a signal that we are + * no longer occluded, and should update our state accordingly. + * + * TODO(b/326464548): Restore dream specific animation. + */ + val unoccludeAnimationRunner: IRemoteAnimationRunner = + object : IRemoteAnimationRunner.Stub() { + var unoccludeAnimator: ValueAnimator? = null + val unoccludeMatrix = Matrix() + + /** TODO(b/326470033): Extract this logic into ViewModels. */ + override fun onAnimationStart( + transit: Int, + apps: Array<RemoteAnimationTarget>, + wallpapers: Array<RemoteAnimationTarget>, + nonApps: Array<RemoteAnimationTarget>, + finishedCallback: IRemoteAnimationFinishedCallback? + ) { + Log.d(TAG, "unoccludeAnimationRunner#onAnimationStart") + // Wrap the callback so that it's guaranteed to be nulled out once called. + unoccludeAnimationFinishedCallback = + object : IRemoteAnimationFinishedCallback.Stub() { + override fun onAnimationFinished() { + finishedCallback?.onAnimationFinished() + unoccludeAnimationFinishedCallback = null + } + } + keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop( + showWhenLockedActivityOnTop = false, + taskInfo = apps.firstOrNull()?.taskInfo, + ) + interactionJankMonitor.begin( + createInteractionJankMonitorConf( + InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION, + "UNOCCLUDE" + ) + ) + if (apps.isEmpty()) { + Log.d( + TAG, + "No apps provided to unocclude runner; " + + "skipping animation and unoccluding." + ) + unoccludeAnimationFinishedCallback?.onAnimationFinished() + return + } + val target = apps[0] + val localView: View = keyguardViewController.get().getViewRootImpl().getView() + val applier = SyncRtSurfaceTransactionApplier(localView) + // TODO( + executor.execute { + unoccludeAnimator?.cancel() + unoccludeAnimator = + ValueAnimator.ofFloat(1f, 0f).apply { + duration = UNOCCLUDE_ANIMATION_DURATION.toLong() + interpolator = Interpolators.TOUCH_RESPONSE + addUpdateListener { animation: ValueAnimator -> + val animatedValue = animation.animatedValue as Float + val surfaceHeight: Float = + target.screenSpaceBounds.height().toFloat() + + unoccludeMatrix.setTranslate( + 0f, + (1f - animatedValue) * + surfaceHeight * + UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT + ) + + SurfaceParams.Builder(target.leash) + .withAlpha(animatedValue) + .withMatrix(unoccludeMatrix) + .withCornerRadius(windowCornerRadius) + .build() + .also { applier.scheduleApply(it) } + } + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + try { + unoccludeAnimationFinishedCallback + ?.onAnimationFinished() + unoccludeAnimator = null + interactionJankMonitor.end( + InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION + ) + } catch (e: RemoteException) { + e.printStackTrace() + } + } + } + ) + start() + } + } + } + + override fun onAnimationCancelled() { + Log.d(TAG, "unoccludeAnimationRunner#onAnimationCancelled") + context.mainExecutor.execute { unoccludeAnimator?.cancel() } + Log.d(TAG, "Unocclude animation cancelled.") + interactionJankMonitor.cancel(InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION) + } + } + + /** + * Called when Window Manager tells the KeyguardService directly that we're occluded or not + * occluded, without starting an occlude/unocclude remote animation. This happens if occlusion + * state changes without an animation (such as if a SHOW_WHEN_LOCKED activity is launched while + * we're unlocked), or if an animation has been cancelled/interrupted and Window Manager wants + * to make sure that we're in the correct state. + */ + fun onKeyguardServiceSetOccluded(occluded: Boolean) { + Log.d(TAG, "#onKeyguardServiceSetOccluded($occluded)") + keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(occluded) + } + + @VisibleForTesting + val occludeAnimationController: ActivityTransitionAnimator.Controller = + object : ActivityTransitionAnimator.Controller { + + override var transitionContainer: ViewGroup + get() = keyguardViewController.get().getViewRootImpl().view as ViewGroup + set(_) { + // Should never be set. + } + + /** TODO(b/326470033): Extract this logic into ViewModels. */ + override fun createAnimatorState(): TransitionAnimator.State { + val fullWidth = transitionContainer.width + val fullHeight = transitionContainer.height + + if ( + keyguardOcclusionInteractor.showWhenLockedActivityLaunchedFromPowerGesture.value + ) { + val initialHeight = fullHeight / 3f + val initialWidth = fullWidth / 3f + + // Start the animation near the power button, at one-third size, since the + // camera was launched from the power button. + return TransitionAnimator.State( + top = (powerButtonY - initialHeight / 2f).toInt(), + bottom = (powerButtonY + initialHeight / 2f).toInt(), + left = (fullWidth - initialWidth).toInt(), + right = fullWidth, + topCornerRadius = windowCornerRadius, + bottomCornerRadius = windowCornerRadius, + ) + } else { + val initialHeight = fullHeight / 2f + val initialWidth = fullWidth / 2f + + // Start the animation in the center of the screen, scaled down to half + // size. + return TransitionAnimator.State( + top = (fullHeight - initialHeight).toInt() / 2, + bottom = (initialHeight + (fullHeight - initialHeight) / 2).toInt(), + left = (fullWidth - initialWidth).toInt() / 2, + right = (initialWidth + (fullWidth - initialWidth) / 2).toInt(), + topCornerRadius = windowCornerRadius, + bottomCornerRadius = windowCornerRadius, + ) + } + } + } + + private fun createInteractionJankMonitorConf( + cuj: Int, + tag: String? + ): InteractionJankMonitor.Configuration.Builder { + val builder = + InteractionJankMonitor.Configuration.Builder.withView( + cuj, + keyguardViewController.get().getViewRootImpl().view + ) + return if (tag != null) builder.setTag(tag) else builder + } + + companion object { + val TAG = "WindowManagerOcclusion" + } +} 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 968c3e3a6792..5306645bf69f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -50,6 +50,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager; +import com.android.systemui.keyguard.WindowManagerOcclusionManager; import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule; import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; @@ -163,7 +164,8 @@ public interface KeyguardModule { SystemPropertiesHelper systemPropertiesHelper, Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager, SelectedUserInteractor selectedUserInteractor, - KeyguardInteractor keyguardInteractor) { + KeyguardInteractor keyguardInteractor, + WindowManagerOcclusionManager windowManagerOcclusionManager) { return new KeyguardViewMediator( context, uiEventLogger, @@ -209,7 +211,8 @@ public interface KeyguardModule { systemPropertiesHelper, wmLockscreenVisibilityManager, selectedUserInteractor, - keyguardInteractor); + keyguardInteractor, + windowManagerOcclusionManager); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepository.kt new file mode 100644 index 000000000000..e3654b415017 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepository.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2024 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.data.repository + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +/** + * Information about the SHOW_WHEN_LOCKED activity that is either newly on top of the task stack, or + * newly not on top of the task stack. + */ +data class ShowWhenLockedActivityInfo( + /** Whether the activity is on top. If not, we're unoccluding and will be animating it out. */ + val isOnTop: Boolean, + + /** + * Information about the activity, which we use for transition internals and also to customize + * animations. + */ + val taskInfo: RunningTaskInfo? = null +) { + fun isDream(): Boolean { + return taskInfo?.topActivityType == WindowConfiguration.ACTIVITY_TYPE_DREAM + } +} + +/** + * Maintains state about "occluding" activities - activities with FLAG_SHOW_WHEN_LOCKED, which are + * capable of displaying over the lockscreen while the device is still locked (such as Google Maps + * navigation). + * + * Window Manager considers the device to be in the "occluded" state whenever such an activity is on + * top of the task stack, including while we're unlocked, while keyguard code considers us to be + * occluded only when we're locked, with an occluding activity currently displaying over the + * lockscreen. + * + * This dual definition is confusing, so this repository collects all of the signals WM gives us, + * and consolidates them into [showWhenLockedActivityInfo.isOnTop], which is the actual question WM + * is answering when they say whether we're 'occluded'. Keyguard then uses this signal to + * conditionally transition to [KeyguardState.OCCLUDED] where appropriate. + */ +@SysUISingleton +class KeyguardOcclusionRepository @Inject constructor() { + val showWhenLockedActivityInfo = MutableStateFlow(ShowWhenLockedActivityInfo(isOnTop = false)) + + /** + * Sets whether there's a SHOW_WHEN_LOCKED activity on top of the task stack, and optionally, + * information about the activity itself. + * + * If no value is provided for [taskInfo], we'll default to the current [taskInfo]. + * + * The [taskInfo] is always present when this method is called from the occlude/unocclude + * animation runners. We use the default when calling from [KeyguardService.isOccluded], since + * we only receive a true/false value there. isOccluded is mostly redundant - it's almost always + * called with true after an occlusion animation has started, and with false after an unocclude + * animation has started. In those cases, we don't want to clear out the taskInfo just because + * it wasn't available at that call site. + */ + fun setShowWhenLockedActivityInfo( + onTop: Boolean, + taskInfo: RunningTaskInfo? = showWhenLockedActivityInfo.value.taskInfo + ) { + showWhenLockedActivityInfo.value = + ShowWhenLockedActivityInfo( + isOnTop = onTop, + taskInfo = taskInfo, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 64e28700aa74..3a6423d680c6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -95,6 +95,7 @@ interface KeyguardRepository { val isKeyguardShowing: Flow<Boolean> /** Is an activity showing over the keyguard? */ + @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED") val isKeyguardOccluded: Flow<Boolean> /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt index 9b3f13d16911..d9479de7e25b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt @@ -75,7 +75,6 @@ constructor( powerInteractor: PowerInteractor, private val scrimLogger: ScrimLogger, ) : LightRevealScrimRepository { - companion object { val TAG = LightRevealScrimRepository::class.simpleName!! } @@ -156,7 +155,11 @@ constructor( override fun startRevealAmountAnimator(reveal: Boolean) { if (reveal == willBeOrIsRevealed) return willBeOrIsRevealed = reveal - if (reveal) revealAmountAnimator.start() else revealAmountAnimator.reverse() + if (reveal && !revealAmountAnimator.isRunning) { + revealAmountAnimator.start() + } else { + revealAmountAnimator.reverse() + } scrimLogger.d(TAG, "startRevealAmountAnimator, reveal: ", reveal) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 6aed944ac809..88ddfd4f4347 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -21,6 +21,7 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor @@ -46,13 +47,16 @@ constructor( @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val communalInteractor: CommunalInteractor, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.ALTERNATE_BOUNCER, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { @@ -112,6 +116,11 @@ constructor( } private fun listenForAlternateBouncerToGone() { + if (KeyguardWmStateRefactor.isEnabled) { + // Handled via #dismissAlternateBouncer. + return + } + scope.launch { keyguardInteractor.isKeyguardGoingAway .sampleUtil(finishedKeyguardState, ::Pair) @@ -149,6 +158,10 @@ constructor( } } + fun dismissAlternateBouncer() { + scope.launch { startTransitionTo(KeyguardState.GONE) } + } + companion object { const val TAG = "FromAlternateBouncerTransitionInteractor" val TRANSITION_DURATION_MS = 300.milliseconds diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index dbd5e26eacfc..9040e031d54e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -27,14 +27,17 @@ import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.util.kotlin.Utils.Companion.sample import com.android.systemui.util.kotlin.sample +import java.util.UUID import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @SysUISingleton @@ -47,29 +50,118 @@ constructor( @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.AOD, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { - listenForAodToLockscreen() + listenForAodToAwake() + listenForAodToOccluded() listenForAodToPrimaryBouncer() listenForAodToGone() - listenForAodToOccluded() listenForTransitionToCamera(scope, keyguardInteractor) } /** + * Listen for the signal that we're waking up and figure what state we need to transition to. + */ + private fun listenForAodToAwake() { + val transitionToLockscreen: suspend (TransitionStep) -> UUID? = + { startedStep: TransitionStep -> + val modeOnCanceled = + if (startedStep.from == KeyguardState.LOCKSCREEN) { + TransitionModeOnCanceled.REVERSE + } else if (startedStep.from == KeyguardState.GONE) { + TransitionModeOnCanceled.RESET + } else { + TransitionModeOnCanceled.LAST_VALUE + } + startTransitionTo( + toState = KeyguardState.LOCKSCREEN, + modeOnCanceled = modeOnCanceled, + ) + } + + if (KeyguardWmStateRefactor.isEnabled) { + // The refactor uses PowerInteractor's wakefulness, which is the earliest wake signal + // available. We have all of the information we need at this time to make a decision + // about where to transition. + scope.launch { + powerInteractor.detailedWakefulness + // React only to wake events. + .filter { it.isAwake() } + .sample( + startedKeyguardTransitionStep, + keyguardInteractor.biometricUnlockState, + keyguardInteractor.primaryBouncerShowing, + ) + // Make sure we've at least STARTED a transition to AOD. + .filter { (_, startedStep, _, _) -> startedStep.to == KeyguardState.AOD } + .collect { (_, startedStep, biometricUnlockState, primaryBouncerShowing) -> + // Check with the superclass to see if an occlusion transition is needed. + // Also, don't react to wake and unlock events, as we'll be receiving a call + // to #dismissAod() shortly when the authentication completes. + if ( + !maybeStartTransitionToOccludedOrInsecureCamera() && + !isWakeAndUnlock(biometricUnlockState) && + !primaryBouncerShowing + ) { + transitionToLockscreen(startedStep) + } + } + } + } else { + scope.launch { + keyguardInteractor + .dozeTransitionTo(DozeStateModel.FINISH) + .sample( + keyguardInteractor.isKeyguardShowing, + startedKeyguardTransitionStep, + keyguardInteractor.isKeyguardOccluded, + keyguardInteractor.biometricUnlockState, + keyguardInteractor.primaryBouncerShowing, + ) + .collect { + ( + _, + isKeyguardShowing, + lastStartedStep, + occluded, + biometricUnlockState, + primaryBouncerShowing) -> + if ( + lastStartedStep.to == KeyguardState.AOD && + !occluded && + !isWakeAndUnlock(biometricUnlockState) && + isKeyguardShowing && + !primaryBouncerShowing + ) { + transitionToLockscreen(lastStartedStep) + } + } + } + } + } + + /** * There are cases where the transition to AOD begins but never completes, such as tapping power * during an incoming phone call when unlocked. In this case, GONE->AOD should be interrupted to * run AOD->OCCLUDED. */ private fun listenForAodToOccluded() { + if (KeyguardWmStateRefactor.isEnabled) { + // Handled by calls to maybeStartTransitionToOccludedOrInsecureCamera on waking. + return + } + scope.launch { keyguardInteractor.isKeyguardOccluded .sample(startedKeyguardTransitionStep, ::Pair) @@ -84,49 +176,6 @@ constructor( } } - private fun listenForAodToLockscreen() { - scope.launch { - keyguardInteractor - .dozeTransitionTo(DozeStateModel.FINISH) - .sample( - keyguardInteractor.isKeyguardShowing, - startedKeyguardTransitionStep, - keyguardInteractor.isKeyguardOccluded, - keyguardInteractor.biometricUnlockState, - keyguardInteractor.primaryBouncerShowing, - ) - .collect { - ( - _, - isKeyguardShowing, - lastStartedStep, - occluded, - biometricUnlockState, - primaryBouncerShowing) -> - if ( - lastStartedStep.to == KeyguardState.AOD && - !occluded && - !isWakeAndUnlock(biometricUnlockState) && - isKeyguardShowing && - !primaryBouncerShowing - ) { - val modeOnCanceled = - if (lastStartedStep.from == KeyguardState.LOCKSCREEN) { - TransitionModeOnCanceled.REVERSE - } else if (lastStartedStep.from == KeyguardState.GONE) { - TransitionModeOnCanceled.RESET - } else { - TransitionModeOnCanceled.LAST_VALUE - } - startTransitionTo( - toState = KeyguardState.LOCKSCREEN, - modeOnCanceled = modeOnCanceled, - ) - } - } - } - } - /** * If there is a biometric lockout and FPS is tapped while on AOD, it should go directly to the * PRIMARY_BOUNCER. @@ -145,6 +194,7 @@ constructor( private fun listenForAodToGone() { if (KeyguardWmStateRefactor.isEnabled) { + // Handled via #dismissAod. return } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 8591fe782d3a..57b2a632008a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -22,6 +22,7 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock import com.android.systemui.keyguard.shared.model.KeyguardState @@ -34,6 +35,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @SysUISingleton @@ -46,18 +48,22 @@ constructor( @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, private val communalInteractor: CommunalInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.DOZING, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { listenForDozingToAny() + listenForWakeFromDozing() listenForTransitionToCamera(scope, keyguardInteractor) } @@ -70,6 +76,10 @@ constructor( } private fun listenForDozingToAny() { + if (KeyguardWmStateRefactor.isEnabled) { + return + } + scope.launch { powerInteractor.isAwake .debounce(50L) @@ -112,6 +122,58 @@ constructor( } } + /** Figure out what state to transition to when we awake from DOZING. */ + private fun listenForWakeFromDozing() { + if (!KeyguardWmStateRefactor.isEnabled) { + return + } + + scope.launch { + powerInteractor.detailedWakefulness + .filter { it.isAwake() } + .sample( + startedKeyguardTransitionStep, + communalInteractor.isIdleOnCommunal, + keyguardInteractor.biometricUnlockState, + canDismissLockScreen, + keyguardInteractor.primaryBouncerShowing, + ) + // If we haven't at least STARTED a transition to DOZING, ignore. + .filter { (_, startedStep, _, _) -> startedStep.to == KeyguardState.DOZING } + .collect { + ( + _, + _, + isIdleOnCommunal, + biometricUnlockState, + canDismissLockscreen, + primaryBouncerShowing) -> + if ( + !maybeStartTransitionToOccludedOrInsecureCamera() && + // Handled by dismissFromDozing(). + !isWakeAndUnlock(biometricUnlockState) + ) { + startTransitionTo( + if (canDismissLockscreen) { + KeyguardState.GONE + } else if (primaryBouncerShowing) { + KeyguardState.PRIMARY_BOUNCER + } else if (isIdleOnCommunal) { + KeyguardState.GLANCEABLE_HUB + } else { + KeyguardState.LOCKSCREEN + } + ) + } + } + } + } + + /** Dismisses keyguard from the DOZING state. */ + fun dismissFromDozing() { + scope.launch { startTransitionTo(KeyguardState.GONE) } + } + override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { return ValueAnimator().apply { interpolator = Interpolators.LINEAR diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt index a6cdaa8c6761..6433d0ede796 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject @@ -46,12 +47,16 @@ constructor( @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, + powerInteractor: PowerInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 28282c630b8f..31371384e338 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -23,10 +23,13 @@ import com.android.systemui.Flags.communalHub import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.util.kotlin.Utils import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample @@ -35,6 +38,7 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @SysUISingleton @@ -48,18 +52,23 @@ constructor( @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val glanceableHubTransitions: GlanceableHubTransitions, + powerInteractor: PowerInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.DREAMING, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { listenForDreamingToOccluded() listenForDreamingToGoneWhenDismissable() listenForDreamingToGoneFromBiometricUnlock() + listenForDreamingToLockscreen() listenForDreamingToAodOrDozing() listenForTransitionToCamera(scope, keyguardInteractor) listenForDreamingToGlanceableHub() @@ -78,6 +87,7 @@ constructor( fun startToLockscreenTransition() { scope.launch { + KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode() if ( transitionInteractor.startedKeyguardState.replayCache.last() == KeyguardState.DREAMING @@ -88,16 +98,52 @@ constructor( } private fun listenForDreamingToOccluded() { + if (KeyguardWmStateRefactor.isEnabled) { + scope.launch { + combine( + keyguardInteractor.isDreaming, + keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, + ::Pair + ) + .sample(startedKeyguardTransitionStep, ::toTriple) + .filter { (isDreaming, _, startedStep) -> + !isDreaming && startedStep.to == KeyguardState.DREAMING + } + .collect { maybeStartTransitionToOccludedOrInsecureCamera() } + } + } else { + scope.launch { + combine( + keyguardInteractor.isKeyguardOccluded, + keyguardInteractor.isDreaming, + ::Pair + ) + .sample(startedKeyguardTransitionStep, Utils.Companion::toTriple) + .collect { (isOccluded, isDreaming, lastStartedTransition) -> + if ( + isOccluded && + !isDreaming && + lastStartedTransition.to == KeyguardState.DREAMING + ) { + startTransitionTo(KeyguardState.OCCLUDED) + } + } + } + } + } + + private fun listenForDreamingToLockscreen() { + if (!KeyguardWmStateRefactor.isEnabled) { + return + } + scope.launch { - combine(keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isDreaming, ::Pair) - .sample(startedKeyguardTransitionStep, ::toTriple) - .collect { (isOccluded, isDreaming, lastStartedTransition) -> - if ( - isOccluded && - !isDreaming && - lastStartedTransition.to == KeyguardState.DREAMING - ) { - startTransitionTo(KeyguardState.OCCLUDED) + keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop + .filter { onTop -> !onTop } + .sample(startedKeyguardState) + .collect { startedState -> + if (startedState == KeyguardState.DREAMING) { + startTransitionTo(KeyguardState.LOCKSCREEN) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 786c3c6697d9..51bc3ae778e5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled @@ -35,6 +36,7 @@ import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -49,14 +51,18 @@ constructor( private val keyguardInteractor: KeyguardInteractor, override val transitionRepository: KeyguardTransitionRepository, transitionInteractor: KeyguardTransitionInteractor, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.GLANCEABLE_HUB, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { + override fun start() { if (!Flags.communalHub()) { return @@ -151,14 +157,27 @@ constructor( } private fun listenForHubToOccluded() { - scope.launch { - and(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming)) - .sample(startedKeyguardState, ::Pair) - .collect { (isOccludedAndNotDreaming, keyguardState) -> - if (isOccludedAndNotDreaming && keyguardState == fromState) { - startTransitionTo(KeyguardState.OCCLUDED) + if (KeyguardWmStateRefactor.isEnabled) { + scope.launch { + keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop + .filter { onTop -> onTop } + .sample(startedKeyguardState) + .collect { + if (it == KeyguardState.GLANCEABLE_HUB) { + maybeStartTransitionToOccludedOrInsecureCamera() + } } - } + } + } else { + scope.launch { + and(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming)) + .sample(startedKeyguardState, ::Pair) + .collect { (isOccludedAndNotDreaming, keyguardState) -> + if (isOccludedAndNotDreaming && keyguardState == fromState) { + startTransitionTo(KeyguardState.OCCLUDED) + } + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 7593ac252543..d5a9bd19d766 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -22,6 +22,8 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.KeyguardWmStateRefactor +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled @@ -34,6 +36,8 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @SysUISingleton @@ -46,14 +50,18 @@ constructor( @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, private val communalInteractor: CommunalInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, + private val biometricSettingsRepository: BiometricSettingsRepository, ) : TransitionInteractor( fromState = KeyguardState.GONE, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { @@ -65,23 +73,46 @@ constructor( // Primarily for when the user chooses to lock down the device private fun listenForGoneToLockscreenOrHub() { - scope.launch { - keyguardInteractor.isKeyguardShowing - .sample( - startedKeyguardState, - communalInteractor.isIdleOnCommunal, - ) - .collect { (isKeyguardShowing, startedState, isIdleOnCommunal) -> - if (isKeyguardShowing && startedState == KeyguardState.GONE) { - val to = - if (isIdleOnCommunal) { - KeyguardState.GLANCEABLE_HUB - } else { - KeyguardState.LOCKSCREEN - } - startTransitionTo(to) + if (KeyguardWmStateRefactor.isEnabled) { + scope.launch { + biometricSettingsRepository.isCurrentUserInLockdown + .distinctUntilChanged() + .filter { inLockdown -> inLockdown } + .sample( + startedKeyguardState, + communalInteractor.isIdleOnCommunal, + ) + .collect { (_, startedState, isIdleOnCommunal) -> + if (startedState == KeyguardState.GONE) { + val to = + if (isIdleOnCommunal) { + KeyguardState.GLANCEABLE_HUB + } else { + KeyguardState.LOCKSCREEN + } + startTransitionTo(to, ownerReason = "User initiated lockdown") + } } - } + } + } else { + scope.launch { + keyguardInteractor.isKeyguardShowing + .sample( + startedKeyguardState, + communalInteractor.isIdleOnCommunal, + ) + .collect { (isKeyguardShowing, startedState, isIdleOnCommunal) -> + if (isKeyguardShowing && startedState == KeyguardState.GONE) { + val to = + if (isIdleOnCommunal) { + KeyguardState.GLANCEABLE_HUB + } else { + KeyguardState.LOCKSCREEN + } + startTransitionTo(to) + } + } + } } } @@ -122,24 +153,10 @@ constructor( private fun listenForGoneToAodOrDozing() { scope.launch { - powerInteractor.isAsleep - .sample( - combine( - startedKeyguardTransitionStep, - keyguardInteractor.isAodAvailable, - ::Pair - ), - ::toTriple - ) - .collect { (isAsleep, lastStartedStep, isAodAvailable) -> - if (lastStartedStep.to == KeyguardState.GONE && isAsleep) { - startTransitionTo( - toState = - if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING, - modeOnCanceled = TransitionModeOnCanceled.RESET, - ) - } - } + listenForSleepTransition( + from = KeyguardState.GONE, + modeOnCanceledFromStartedStep = { TransitionModeOnCanceled.RESET }, + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index cb1571e7d702..bcad3324b258 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -33,7 +33,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.Utils.Companion.toQuad -import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import java.util.UUID import javax.inject.Inject @@ -44,6 +43,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart @@ -62,21 +62,24 @@ constructor( private val keyguardInteractor: KeyguardInteractor, private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, private val glanceableHubTransitions: GlanceableHubTransitions, private val swipeToDismissInteractor: SwipeToDismissInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.LOCKSCREEN, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { listenForLockscreenToGone() listenForLockscreenToGoneDragging() - listenForLockscreenToOccluded() + listenForLockscreenToOccludedOrDreaming() listenForLockscreenToAodOrDozing() listenForLockscreenToPrimaryBouncer() listenForLockscreenToDreaming() @@ -115,6 +118,10 @@ constructor( } private fun listenForLockscreenToDreaming() { + if (KeyguardWmStateRefactor.isEnabled) { + return + } + val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING) scope.launch { keyguardInteractor.isAbleToDream @@ -157,7 +164,10 @@ constructor( if ( isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN ) { - startTransitionTo(KeyguardState.PRIMARY_BOUNCER) + startTransitionTo( + KeyguardState.PRIMARY_BOUNCER, + ownerReason = "#listenForLockscreenToPrimaryBouncer" + ) } } } @@ -254,7 +264,8 @@ constructor( transitionId = startTransitionTo( toState = KeyguardState.PRIMARY_BOUNCER, - animator = null, // transition will be manually controlled + animator = null, // transition will be manually controlled, + ownerReason = "#listenForLockscreenToPrimaryBouncerDragging" ) } } @@ -309,47 +320,51 @@ constructor( } } - private fun listenForLockscreenToOccluded() { - scope.launch { - keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect { - (isOccluded, keyguardState) -> - if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) { - startTransitionTo(KeyguardState.OCCLUDED) - } + private fun listenForLockscreenToOccludedOrDreaming() { + if (KeyguardWmStateRefactor.isEnabled) { + scope.launch { + keyguardOcclusionInteractor.showWhenLockedActivityInfo + .filter { it.isOnTop } + .sample(startedKeyguardState, ::Pair) + .collect { (taskInfo, startedState) -> + if (startedState == KeyguardState.LOCKSCREEN) { + startTransitionTo( + if (taskInfo.isDream()) { + KeyguardState.DREAMING + } else { + KeyguardState.OCCLUDED + } + ) + } + } + } + } else { + scope.launch { + keyguardInteractor.isKeyguardOccluded + .sample(startedKeyguardState, ::Pair) + .collect { (isOccluded, keyguardState) -> + if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) { + startTransitionTo(KeyguardState.OCCLUDED) + } + } } } } private fun listenForLockscreenToAodOrDozing() { scope.launch { - powerInteractor.isAsleep - .sample( - combine( - startedKeyguardTransitionStep, - keyguardInteractor.isAodAvailable, - ::Pair - ), - ::toTriple - ) - .collect { (isAsleep, lastStartedStep, isAodAvailable) -> - if (lastStartedStep.to == KeyguardState.LOCKSCREEN && isAsleep) { - val toState = - if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING - val modeOnCanceled = - if ( - toState == KeyguardState.AOD && - lastStartedStep.from == KeyguardState.AOD - ) { - TransitionModeOnCanceled.REVERSE - } else { - TransitionModeOnCanceled.LAST_VALUE - } - startTransitionTo( - toState = toState, - modeOnCanceled = modeOnCanceled, - ) + listenForSleepTransition( + from = KeyguardState.LOCKSCREEN, + modeOnCanceledFromStartedStep = { startedStep -> + if ( + startedStep.to == KeyguardState.AOD && startedStep.from == KeyguardState.AOD + ) { + TransitionModeOnCanceled.REVERSE + } else { + TransitionModeOnCanceled.LAST_VALUE } } + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index efb604d5dadc..f10327e02240 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -22,17 +22,17 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.util.kotlin.Utils.Companion.sample -import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @SysUISingleton @@ -45,20 +45,23 @@ constructor( @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, private val communalInteractor: CommunalInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.OCCLUDED, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { listenForOccludedToLockscreenOrHub() listenForOccludedToDreaming() - listenForOccludedToAodOrDozing() + listenForOccludedToAsleep() listenForOccludedToGone() listenForOccludedToAlternateBouncer() listenForOccludedToPrimaryBouncer() @@ -90,43 +93,72 @@ constructor( } private fun listenForOccludedToLockscreenOrHub() { - scope.launch { - keyguardInteractor.isKeyguardOccluded - .sample( - keyguardInteractor.isKeyguardShowing, - startedKeyguardTransitionStep, - communalInteractor.isIdleOnCommunal, - ) - .collect { (isOccluded, isShowing, lastStartedKeyguardState, isIdleOnCommunal) -> - // Occlusion signals come from the framework, and should interrupt any - // existing transition - if ( - !isOccluded && - isShowing && - lastStartedKeyguardState.to == KeyguardState.OCCLUDED - ) { - val to = - if (isIdleOnCommunal) { - KeyguardState.GLANCEABLE_HUB - } else { - KeyguardState.LOCKSCREEN - } - startTransitionTo(to) + if (KeyguardWmStateRefactor.isEnabled) { + scope.launch { + keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop + .filter { onTop -> !onTop } + .sample( + startedKeyguardState, + communalInteractor.isIdleOnCommunal, + ) + .collect { (_, startedState, isIdleOnCommunal) -> + // Occlusion signals come from the framework, and should interrupt any + // existing transition + if (startedState == KeyguardState.OCCLUDED) { + val to = + if (isIdleOnCommunal) { + KeyguardState.GLANCEABLE_HUB + } else { + KeyguardState.LOCKSCREEN + } + startTransitionTo(to) + } } - } + } + } else { + scope.launch { + keyguardInteractor.isKeyguardOccluded + .sample( + keyguardInteractor.isKeyguardShowing, + startedKeyguardTransitionStep, + communalInteractor.isIdleOnCommunal, + ) + .collect { (isOccluded, isShowing, lastStartedKeyguardState, isIdleOnCommunal) + -> + // Occlusion signals come from the framework, and should interrupt any + // existing transition + if ( + !isOccluded && + isShowing && + lastStartedKeyguardState.to == KeyguardState.OCCLUDED + ) { + val to = + if (isIdleOnCommunal) { + KeyguardState.GLANCEABLE_HUB + } else { + KeyguardState.LOCKSCREEN + } + startTransitionTo(to) + } + } + } } } private fun listenForOccludedToGone() { + if (KeyguardWmStateRefactor.isEnabled) { + // We don't think OCCLUDED to GONE is possible. You should always have to go via a + // *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard + // dismisses it, and even "extend unlock" doesn't unlock the device in the background. + // If we're wrong - sorry, add it back here. + return + } + scope.launch { keyguardInteractor.isKeyguardOccluded .sample( - combine( - keyguardInteractor.isKeyguardShowing, - startedKeyguardTransitionStep, - ::Pair - ), - ::toTriple + keyguardInteractor.isKeyguardShowing, + startedKeyguardTransitionStep, ) .collect { (isOccluded, isShowing, lastStartedKeyguardState) -> // Occlusion signals come from the framework, and should interrupt any @@ -142,25 +174,12 @@ constructor( } } - private fun listenForOccludedToAodOrDozing() { - scope.launch { - powerInteractor.isAsleep - .sample( - combine( - startedKeyguardTransitionStep, - keyguardInteractor.isAodAvailable, - ::Pair - ), - ::toTriple - ) - .collect { (isAsleep, lastStartedStep, isAodAvailable) -> - if (lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep) { - startTransitionTo( - if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING - ) - } - } - } + fun dismissToGone() { + scope.launch { startTransitionTo(KeyguardState.GONE) } + } + + private fun listenForOccludedToAsleep() { + scope.launch { listenForSleepTransition(from = KeyguardState.OCCLUDED) } } private fun listenForOccludedToAlternateBouncer() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index c5a28463bf7e..391dccc7f444 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -31,7 +31,6 @@ import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.kotlin.Utils.Companion.sample -import com.android.systemui.util.kotlin.Utils.Companion.toQuad import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import com.android.wm.shell.animation.Interpolators @@ -42,6 +41,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @@ -59,18 +59,21 @@ constructor( private val flags: FeatureFlags, private val keyguardSecurityModel: KeyguardSecurityModel, private val selectedUserInteractor: SelectedUserInteractor, - private val powerInteractor: PowerInteractor, + powerInteractor: PowerInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : TransitionInteractor( fromState = KeyguardState.PRIMARY_BOUNCER, transitionInteractor = transitionInteractor, mainDispatcher = mainDispatcher, bgDispatcher = bgDispatcher, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) { override fun start() { listenForPrimaryBouncerToGone() - listenForPrimaryBouncerToAodOrDozing() + listenForPrimaryBouncerToAsleep() listenForPrimaryBouncerToLockscreenHubOrOccluded() listenForPrimaryBouncerToDreamingLockscreenHosted() listenForTransitionToCamera(scope, keyguardInteractor) @@ -128,74 +131,88 @@ constructor( } private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() { - scope.launch { - keyguardInteractor.primaryBouncerShowing - .sample( - powerInteractor.isAwake, - startedKeyguardTransitionStep, - keyguardInteractor.isKeyguardOccluded, - keyguardInteractor.isDreaming, - keyguardInteractor.isActiveDreamLockscreenHosted, - communalInteractor.isIdleOnCommunal, - ) - .collect { - ( - isBouncerShowing, - isAwake, - lastStartedTransitionStep, - occluded, - isDreaming, - isActiveDreamLockscreenHosted, - isIdleOnCommunal) -> - if ( - !isBouncerShowing && - lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER && - isAwake && - !isActiveDreamLockscreenHosted - ) { - val toState = - if (occluded && !isDreaming) { - KeyguardState.OCCLUDED - } else if (isIdleOnCommunal) { - KeyguardState.GLANCEABLE_HUB - } else if (isDreaming) { - KeyguardState.DREAMING - } else { - KeyguardState.LOCKSCREEN - } - startTransitionTo(toState) + if (KeyguardWmStateRefactor.isEnabled) { + scope.launch { + keyguardInteractor.primaryBouncerShowing + .sample( + startedKeyguardTransitionStep, + powerInteractor.isAwake, + keyguardInteractor.isActiveDreamLockscreenHosted, + communalInteractor.isIdleOnCommunal + ) + .filter { (_, startedStep, _, _) -> + startedStep.to == KeyguardState.PRIMARY_BOUNCER } - } - } - } - - private fun listenForPrimaryBouncerToAodOrDozing() { - scope.launch { - keyguardInteractor.primaryBouncerShowing - .sample( - combine( - powerInteractor.isAsleep, + .collect { + ( + isBouncerShowing, + _, + isAwake, + isActiveDreamLockscreenHosted, + isIdleOnCommunal) -> + if ( + !maybeStartTransitionToOccludedOrInsecureCamera() && + !isBouncerShowing && + isAwake && + !isActiveDreamLockscreenHosted + ) { + val toState = + if (isIdleOnCommunal) { + KeyguardState.GLANCEABLE_HUB + } else { + KeyguardState.LOCKSCREEN + } + startTransitionTo(toState) + } + } + } + } else { + scope.launch { + keyguardInteractor.primaryBouncerShowing + .sample( + powerInteractor.isAwake, startedKeyguardTransitionStep, - keyguardInteractor.isAodAvailable, - ::Triple - ), - ::toQuad - ) - .collect { (isBouncerShowing, isAsleep, lastStartedTransitionStep, isAodAvailable) - -> - if ( - !isBouncerShowing && - lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER && - isAsleep - ) { - startTransitionTo( - if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING - ) + keyguardInteractor.isKeyguardOccluded, + keyguardInteractor.isDreaming, + keyguardInteractor.isActiveDreamLockscreenHosted, + communalInteractor.isIdleOnCommunal, + ) + .collect { + ( + isBouncerShowing, + isAwake, + lastStartedTransitionStep, + occluded, + isDreaming, + isActiveDreamLockscreenHosted, + isIdleOnCommunal) -> + if ( + !isBouncerShowing && + lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER && + isAwake && + !isActiveDreamLockscreenHosted + ) { + val toState = + if (occluded && !isDreaming) { + KeyguardState.OCCLUDED + } else if (isIdleOnCommunal) { + KeyguardState.GLANCEABLE_HUB + } else if (isDreaming) { + KeyguardState.DREAMING + } else { + KeyguardState.LOCKSCREEN + } + startTransitionTo(toState) + } } - } + } } } + private fun listenForPrimaryBouncerToAsleep() { + scope.launch { listenForSleepTransition(from = KeyguardState.PRIMARY_BOUNCER) } + } + private fun listenForPrimaryBouncerToDreamingLockscreenHosted() { scope.launch { keyguardInteractor.primaryBouncerShowing diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 5410b10a4b93..f321bd7e13a0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -163,15 +163,18 @@ constructor( .distinctUntilChanged() /** Whether the keyguard is showing or not. */ + @Deprecated("Use KeyguardTransitionInteractor + KeyguardState") val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing /** Whether the keyguard is dismissible or not. */ val isKeyguardDismissible: Flow<Boolean> = repository.isKeyguardDismissible /** Whether the keyguard is occluded (covered by an activity). */ + @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED") val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded /** Whether the keyguard is going away. */ + @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.GONE") val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway /** Keyguard can be clipped at the top as the shade is dragged */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt new file mode 100644 index 000000000000..9aa2202b4100 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 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.domain.interactor + +import android.app.ActivityManager.RunningTaskInfo +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.repository.KeyguardOcclusionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.util.kotlin.sample +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.stateIn + +/** + * Logic related to keyguard occlusion. The keyguard is occluded when an activity with + * FLAG_SHOW_WHEN_LOCKED is on top of the activity task stack, with that activity displaying on top + * of ("occluding") the lockscreen UI. Common examples of this are Google Maps Navigation and the + * secure camera. + * + * This should usually be used only by keyguard internal classes. Most System UI use cases should + * use [KeyguardTransitionInteractor] to see if we're in [KeyguardState.OCCLUDED] instead. + */ +@SysUISingleton +class KeyguardOcclusionInteractor +@Inject +constructor( + @Application scope: CoroutineScope, + val repository: KeyguardOcclusionRepository, + val powerInteractor: PowerInteractor, + val transitionInteractor: KeyguardTransitionInteractor, + val keyguardInteractor: KeyguardInteractor, +) { + val showWhenLockedActivityInfo = repository.showWhenLockedActivityInfo.asStateFlow() + + /** + * Whether a SHOW_WHEN_LOCKED activity is on top of the task stack. This does not necessarily + * mean we're OCCLUDED, as we could be GONE (unlocked), with an activity that can (but is not + * currently) displaying over the lockscreen. + * + * Transition interactors use this to determine when we should transition to the OCCLUDED state. + * + * Outside of the transition/occlusion interactors, you almost certainly don't want to use this. + * Instead, use KeyguardTransitionInteractor to figure out if we're in KeyguardState.OCCLUDED. + */ + val isShowWhenLockedActivityOnTop = showWhenLockedActivityInfo.map { it.isOnTop } + + /** Whether we should start a transition due to the power button launch gesture. */ + fun shouldTransitionFromPowerButtonGesture(): Boolean { + // powerButtonLaunchGestureTriggered remains true while we're awake from a power button + // gesture. Check that we were asleep or transitioning to asleep before starting a + // transition, to ensure we don't transition while moving between, for example, + // *_BOUNCER -> LOCKSCREEN. + return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered && + KeyguardState.deviceIsAsleepInState(transitionInteractor.getStartedState()) + } + + /** + * Whether the SHOW_WHEN_LOCKED activity was launched from the double tap power button gesture. + * This remains true while the activity is running and emits false once it is killed. + */ + val showWhenLockedActivityLaunchedFromPowerGesture = + merge( + // Emit true when the power launch gesture is triggered, since this means a + // SHOW_WHEN_LOCKED activity will be launched from the gesture (unless we're + // currently + // GONE, in which case we're going back to GONE and launching the insecure camera). + powerInteractor.detailedWakefulness + .sample(transitionInteractor.currentKeyguardState, ::Pair) + .map { (wakefulness, currentKeyguardState) -> + wakefulness.powerButtonLaunchGestureTriggered && + currentKeyguardState != KeyguardState.GONE + }, + // Emit false once that activity goes away. + isShowWhenLockedActivityOnTop.filter { !it }.map { false } + ) + .stateIn(scope, SharingStarted.Eagerly, false) + + /** + * Whether launching an occluding activity will automatically dismiss keyguard. This happens if + * the keyguard is dismissable. + */ + val occludingActivityWillDismissKeyguard = + keyguardInteractor.isKeyguardDismissible.stateIn(scope, SharingStarted.Eagerly, false) + + /** + * Called to let System UI know that WM says a SHOW_WHEN_LOCKED activity is on top (or no longer + * on top). + * + * This signal arrives from WM when a SHOW_WHEN_LOCKED activity is started or killed - it is + * never set directly by System UI. While we might be the reason the activity was started + * (launching the camera from the power button gesture), we ultimately only receive this signal + * once that activity starts. It's up to us to start the appropriate keyguard transitions, + * because that activity is going to be visible (or not) regardless. + */ + fun setWmNotifiedShowWhenLockedActivityOnTop( + showWhenLockedActivityOnTop: Boolean, + taskInfo: RunningTaskInfo? = null + ) { + repository.setShowWhenLockedActivityInfo(showWhenLockedActivityOnTop, taskInfo) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index d81f1f14158c..c28e49db9d37 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -41,6 +41,7 @@ constructor( private val powerInteractor: PowerInteractor, private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, private val shadeInteractor: ShadeInteractor, + private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) { fun start() { @@ -91,6 +92,12 @@ constructor( } scope.launch { + keyguardInteractor.isKeyguardDismissible.collect { + logger.log(TAG, VERBOSE, "isKeyguardDismissable", it) + } + } + + scope.launch { keyguardInteractor.isAbleToDream.collect { logger.log(TAG, VERBOSE, "isAbleToDream", it) } @@ -125,5 +132,11 @@ constructor( logger.log(TAG, VERBOSE, "onCameraLaunchDetected", it) } } + + scope.launch { + keyguardOcclusionInteractor.showWhenLockedActivityInfo.collect { + logger.log(TAG, VERBOSE, "showWhenLockedActivityInfo", it) + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 37b331cd8455..00902b419a97 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.domain.interactor import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER @@ -42,6 +43,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -56,11 +58,15 @@ class KeyguardTransitionInteractor @Inject constructor( @Application val scope: CoroutineScope, + private val keyguardRepository: KeyguardRepository, private val repository: KeyguardTransitionRepository, private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>, private val fromPrimaryBouncerTransitionInteractor: dagger.Lazy<FromPrimaryBouncerTransitionInteractor>, private val fromAodTransitionInteractor: dagger.Lazy<FromAodTransitionInteractor>, + private val fromAlternateBouncerTransitionInteractor: + dagger.Lazy<FromAlternateBouncerTransitionInteractor>, + private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>, ) { private val TAG = this::class.simpleName @@ -207,6 +213,12 @@ constructor( .map { step -> step.to } .shareIn(scope, SharingStarted.Eagerly, replay = 1) + /** Which keyguard state to use when the device goes to sleep. */ + val asleepKeyguardState: StateFlow<KeyguardState> = + keyguardRepository.isAodAvailable + .map { aodAvailable -> if (aodAvailable) AOD else DOZING } + .stateIn(scope, SharingStarted.Eagerly, DOZING) + /** * A pair of the most recent STARTED step, and the transition step immediately preceding it. The * transition framework enforces that the previous step is either a CANCELED or FINISHED step, @@ -368,7 +380,10 @@ constructor( when (val startedState = startedKeyguardState.replayCache.last()) { LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard() PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer() + ALTERNATE_BOUNCER -> + fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer() AOD -> fromAodTransitionInteractor.get().dismissAod() + DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing() else -> Log.e( "KeyguardTransitionInteractor", @@ -421,12 +436,17 @@ constructor( fromStatePredicate: (KeyguardState) -> Boolean, toStatePredicate: (KeyguardState) -> Boolean, ): Flow<Boolean> { + return isInTransitionWhere { from, to -> fromStatePredicate(from) && toStatePredicate(to) } + } + + fun isInTransitionWhere( + fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean + ): Flow<Boolean> { return repository.transitions .filter { it.transitionState != TransitionState.CANCELED } .mapLatest { it.transitionState != TransitionState.FINISHED && - fromStatePredicate(it.from) && - toStatePredicate(it.to) + fromToStatePredicate(it.from, it.to) } .distinctUntilChanged() } @@ -447,4 +467,16 @@ constructor( */ fun isFinishedInStateWhereValue(stateMatcher: (KeyguardState) -> Boolean) = stateMatcher(finishedKeyguardState.replayCache.last()) + + fun getCurrentState(): KeyguardState { + return currentKeyguardState.replayCache.last() + } + + fun getStartedState(): KeyguardState { + return startedKeyguardState.replayCache.last() + } + + fun getFinishedState(): KeyguardState { + return finishedKeyguardState.replayCache.last() + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt index 4d731eccd9bb..8905c9e752de 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt @@ -43,7 +43,6 @@ constructor( private val scrimLogger: ScrimLogger, private val powerInteractor: PowerInteractor, ) { - init { listenForStartedKeyguardTransitionStep() } @@ -52,9 +51,7 @@ constructor( scope.launch { transitionInteractor.startedKeyguardTransitionStep.collect { scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it) - lightRevealScrimRepository.startRevealAmountAnimator( - willBeRevealedInState(it.to), - ) + lightRevealScrimRepository.startRevealAmountAnimator(willBeRevealedInState(it.to)) } } } @@ -89,25 +86,25 @@ constructor( companion object { - /** - * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given - * state after the transition is complete. If false, scrim will be fully hidden. - */ - private fun willBeRevealedInState(state: KeyguardState): Boolean { - return when (state) { - KeyguardState.OFF -> false - KeyguardState.DOZING -> false - KeyguardState.AOD -> false - KeyguardState.DREAMING -> true - KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true - KeyguardState.GLANCEABLE_HUB -> true - KeyguardState.ALTERNATE_BOUNCER -> true - KeyguardState.PRIMARY_BOUNCER -> true - KeyguardState.LOCKSCREEN -> true - KeyguardState.GONE -> true - KeyguardState.OCCLUDED -> true + /** + * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given + * state after the transition is complete. If false, scrim will be fully hidden. + */ + private fun willBeRevealedInState(state: KeyguardState): Boolean { + return when (state) { + KeyguardState.OFF -> false + KeyguardState.DOZING -> false + KeyguardState.AOD -> false + KeyguardState.DREAMING -> true + KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true + KeyguardState.GLANCEABLE_HUB -> true + KeyguardState.ALTERNATE_BOUNCER -> true + KeyguardState.PRIMARY_BOUNCER -> true + KeyguardState.LOCKSCREEN -> true + KeyguardState.GONE -> true + KeyguardState.OCCLUDED -> true + } } - } val TAG = LightRevealScrimInteractor::class.simpleName!! } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index 3ccbdba6d58e..375df3e8f5f5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -18,15 +18,20 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import android.util.Log +import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.util.kotlin.sample import java.util.UUID import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -46,6 +51,8 @@ sealed class TransitionInteractor( val transitionInteractor: KeyguardTransitionInteractor, val mainDispatcher: CoroutineDispatcher, val bgDispatcher: CoroutineDispatcher, + val powerInteractor: PowerInteractor, + val keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) { val name = this::class.simpleName ?: "UnknownTransitionInteractor" abstract val transitionRepository: KeyguardTransitionRepository @@ -65,7 +72,11 @@ sealed class TransitionInteractor( suspend fun startTransitionTo( toState: KeyguardState, animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState), - modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE + modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE, + // Even more information about why the owner started this transition, if this is a dangerous + // transition (*cough* occlusion) where you'd be sad to not have all the info you can get in + // a bugreport. + ownerReason: String = "", ): UUID? { if ( fromState != transitionInteractor.startedKeyguardState.replayCache.last() && @@ -85,7 +96,7 @@ sealed class TransitionInteractor( return withContext(mainDispatcher) { transitionRepository.startTransition( TransitionInfo( - name, + name + if (ownerReason.isNotBlank()) "($ownerReason)" else "", fromState, toState, animator, @@ -95,24 +106,107 @@ sealed class TransitionInteractor( } } + /** + * Check whether we need to transition to [KeyguardState.OCCLUDED], based on the presence of a + * SHOW_WHEN_LOCKED activity, or back to [KeyguardState.GONE], for some power button launch + * gesture cases. If so, start the transition. + * + * Returns true if a transition was started, false otherwise. + */ + suspend fun maybeStartTransitionToOccludedOrInsecureCamera(): Boolean { + if (keyguardOcclusionInteractor.shouldTransitionFromPowerButtonGesture()) { + if (transitionInteractor.getCurrentState() == KeyguardState.GONE) { + // If the current state is GONE when the launch gesture is triggered, it means we + // were in transition from GONE -> DOZING/AOD due to the first power button tap. The + // second tap indicates that the user's intent was actually to launch the unlocked + // (insecure) camera, so we should transition back to GONE. + startTransitionTo( + KeyguardState.GONE, + ownerReason = "Power button gesture while GONE" + ) + } else if (keyguardOcclusionInteractor.occludingActivityWillDismissKeyguard.value) { + // The double tap gesture occurred while not GONE (AOD/LOCKSCREEN/etc.), but the + // keyguard is dismissable. The activity launch will dismiss the keyguard, so we + // should transition to GONE. + startTransitionTo( + KeyguardState.GONE, + ownerReason = "Power button gesture on dismissable keyguard" + ) + } else { + // Otherwise, the double tap gesture occurred while not GONE and not dismissable, + // which means we will launch the secure camera, which OCCLUDES the keyguard. + startTransitionTo( + KeyguardState.OCCLUDED, + ownerReason = "Power button gesture on lockscreen" + ) + } + + return true + } else if (keyguardOcclusionInteractor.showWhenLockedActivityInfo.value.isOnTop) { + // A SHOW_WHEN_LOCKED activity is on top of the task stack. Transition to OCCLUDED so + // it's visible. + // TODO(b/307976454) - Centralize transition to DREAMING here. + startTransitionTo( + KeyguardState.OCCLUDED, + ownerReason = "SHOW_WHEN_LOCKED activity on top" + ) + + return true + } else { + // No transition needed, let the interactor figure out where to go. + return false + } + } + + /** + * Transition to the appropriate state when the device goes to sleep while in [from]. + * + * We could also just use [fromState], but it's more readable in the From*TransitionInteractor + * if you're explicitly declaring which state you're listening from. If you passed in the wrong + * state, [startTransitionTo] would complain anyway. + */ + suspend fun listenForSleepTransition( + from: KeyguardState, + modeOnCanceledFromStartedStep: (TransitionStep) -> TransitionModeOnCanceled = { + TransitionModeOnCanceled.LAST_VALUE + } + ) { + powerInteractor.isAsleep + .filter { isAsleep -> isAsleep } + .sample(startedKeyguardTransitionStep) + .filter { startedStep -> startedStep.to == from } + .map(modeOnCanceledFromStartedStep) + .collect { modeOnCanceled -> + startTransitionTo( + toState = transitionInteractor.asleepKeyguardState.value, + modeOnCanceled = modeOnCanceled, + ownerReason = "Sleep transition triggered" + ) + } + } + /** This signal may come in before the occlusion signal, and can provide a custom transition */ fun listenForTransitionToCamera( scope: CoroutineScope, keyguardInteractor: KeyguardInteractor, ) { - scope.launch { - keyguardInteractor.onCameraLaunchDetected - .sample(transitionInteractor.finishedKeyguardState) - .collect { finishedKeyguardState -> - // Other keyguard state transitions may trigger on the first power button push, - // so use the last finishedKeyguardState to determine the overriding FROM state - if (finishedKeyguardState == fromState) { - startTransitionTo( - toState = KeyguardState.OCCLUDED, - modeOnCanceled = TransitionModeOnCanceled.RESET, - ) + if (!KeyguardWmStateRefactor.isEnabled) { + scope.launch { + keyguardInteractor.onCameraLaunchDetected + .sample(transitionInteractor.finishedKeyguardState) + .collect { finishedKeyguardState -> + // Other keyguard state transitions may trigger on the first power button + // push, + // so use the last finishedKeyguardState to determine the overriding FROM + // state + if (finishedKeyguardState == fromState) { + startTransitionTo( + toState = KeyguardState.OCCLUDED, + modeOnCanceled = TransitionModeOnCanceled.RESET, + ) + } } - } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt index a9eec18319c3..7e39a884a69e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt @@ -43,6 +43,10 @@ constructor( to = KeyguardState.GONE, ) + /** + * AOD -> GONE should fade out the lockscreen contents. This transition plays both during wake + * and unlock, and also during insecure camera launch (which is GONE -> AOD (canceled) -> GONE). + */ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> { var startAlpha = 1f return transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt index 105a7ed52311..445575f7e55d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt @@ -16,12 +16,15 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow /** Breaks down AOD->OCCLUDED transition into discrete steps for corresponding views to consume. */ @SysUISingleton @@ -37,5 +40,23 @@ constructor( to = KeyguardState.OCCLUDED, ) + /** + * Fade out the lockscreen during a transition to OCCLUDED. + * + * This happens when pressing the power button while a SHOW_WHEN_LOCKED activity is on the top + * of the task stack, as well as when the power button is double tapped on the LOCKSCREEN (the + * first tap transitions to AOD, the second cancels that transition and starts AOD -> OCCLUDED. + */ + fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> { + var currentAlpha = 0f + return transitionAnimation.sharedFlow( + duration = 250.milliseconds, + startTime = 100.milliseconds, // Wait for the light reveal to "hit" the LS elements. + onStart = { currentAlpha = viewState.alpha() }, + onStep = { MathUtils.lerp(currentAlpha, 0f, it) }, + onCancel = { 0f }, + ) + } + override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt new file mode 100644 index 000000000000..c0b11959cbd9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 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.ui.viewmodel + +import android.util.MathUtils +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +/** + * Breaks down DOZING->OCCLUDED transition into discrete steps for corresponding views to consume. + */ +@SysUISingleton +class DozingToOccludedTransitionViewModel +@Inject +constructor( + animationFlow: KeyguardTransitionAnimationFlow, +) : DeviceEntryIconTransition { + private val transitionAnimation = + animationFlow.setup( + duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION, + from = KeyguardState.DOZING, + to = KeyguardState.OCCLUDED, + ) + + /** + * Fade out the lockscreen during a transition to OCCLUDED. + * + * This happens when pressing the power button while a SHOW_WHEN_LOCKED activity is on the top + * of the task stack, as well as when the power button is double tapped on the LOCKSCREEN (the + * first tap transitions to DOZING, the second cancels that transition and starts DOZING -> + * OCCLUDED. + */ + fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> { + var currentAlpha = 0f + return transitionAnimation.sharedFlow( + duration = 250.milliseconds, + startTime = 100.milliseconds, // Wait for the light reveal to "hit" the LS elements. + onStart = { currentAlpha = viewState.alpha() }, + onStep = { MathUtils.lerp(currentAlpha, 0f, it) }, + onCancel = { 0f }, + ) + } + + override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 1760b927a6cd..f848717c2170 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -73,8 +73,10 @@ constructor( AlternateBouncerToGoneTransitionViewModel, private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel, private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, + private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel, private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel, private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel, + private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel, private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, private val glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel, @@ -170,8 +172,10 @@ constructor( alternateBouncerToGoneTransitionViewModel.lockscreenAlpha, aodToGoneTransitionViewModel.lockscreenAlpha(viewState), aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState), + aodToOccludedTransitionViewModel.lockscreenAlpha(viewState), dozingToGoneTransitionViewModel.lockscreenAlpha(viewState), dozingToLockscreenTransitionViewModel.lockscreenAlpha, + dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState), dreamingToLockscreenTransitionViewModel.lockscreenAlpha, glanceableHubToLockscreenTransitionViewModel.keyguardAlpha, goneToAodTransitionViewModel.enterFromTopAnimationAlpha, diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt index e1d1ec207938..5432793d8117 100644 --- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt +++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt @@ -20,20 +20,24 @@ data class WakefulnessModel( * the [KeyguardTransitionInteractor]. */ internal val internalWakefulnessState: WakefulnessState = WakefulnessState.AWAKE, - val lastWakeReason: WakeSleepReason = WakeSleepReason.OTHER, val lastSleepReason: WakeSleepReason = WakeSleepReason.OTHER, - /** + /** * Whether the power button double tap gesture was triggered since the last time went to sleep. * If this value is true while [isAsleep]=true, it means we'll be waking back up shortly. If it * is true while [isAwake]=true, it means we're awake because of the button gesture. * - * This value remains true until the next time [isAsleep]=true. + * This value remains true until the next time [isAsleep]=true, since it would otherwise be + * totally arbitrary at what point we decide the gesture was no longer "triggered". Since a + * sleep event is guaranteed to arrive prior to the next power button gesture (as the first tap + * of the double tap always begins a sleep transition), this will always be reset to false prior + * to a subsequent power gesture. */ val powerButtonLaunchGestureTriggered: Boolean = false, ) { - fun isAwake() = internalWakefulnessState == WakefulnessState.AWAKE || + fun isAwake() = + internalWakefulnessState == WakefulnessState.AWAKE || internalWakefulnessState == WakefulnessState.STARTING_TO_WAKE fun isAsleep() = !isAwake() @@ -48,11 +52,10 @@ data class WakefulnessModel( fun isAsleepFrom(wakeSleepReason: WakeSleepReason) = isAsleep() && lastSleepReason == wakeSleepReason - fun isAwakeOrAsleepFrom(reason: WakeSleepReason) = - isAsleepFrom(reason) || isAwakeFrom(reason) + fun isAwakeOrAsleepFrom(reason: WakeSleepReason) = isAsleepFrom(reason) || isAwakeFrom(reason) fun isAwakeFromTapOrGesture(): Boolean { - return isAwake() && (lastWakeReason == WakeSleepReason.TAP || - lastWakeReason == WakeSleepReason.GESTURE) + return isAwake() && + (lastWakeReason == WakeSleepReason.TAP || lastWakeReason == WakeSleepReason.GESTURE) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt new file mode 100644 index 000000000000..1bebbfd34ff4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 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.statusbar.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.util.kotlin.sample +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +/** + * Whether to set the status bar keyguard view occluded or not, and whether to animate that change. + */ +data class OccludedState( + val occluded: Boolean, + val animate: Boolean = false, +) + +/** Handles logic around calls to [StatusBarKeyguardViewManager] in legacy code. */ +@Deprecated("Will be removed once all of SBKVM's responsibilies are refactored.") +@SysUISingleton +class StatusBarKeyguardViewManagerInteractor +@Inject +constructor( + keyguardTransitionInteractor: KeyguardTransitionInteractor, + keyguardOcclusionInteractor: KeyguardOcclusionInteractor, + powerInteractor: PowerInteractor, +) { + /** Occlusion state to apply whenever a keyguard transition is STARTED, if any. */ + private val occlusionStateFromStartedStep: Flow<OccludedState> = + keyguardTransitionInteractor.startedKeyguardTransitionStep + .sample(powerInteractor.detailedWakefulness, ::Pair) + .map { (startedStep, wakefulness) -> + val transitioningFromPowerButtonGesture = + KeyguardState.deviceIsAsleepInState(startedStep.from) && + startedStep.to == KeyguardState.OCCLUDED && + wakefulness.powerButtonLaunchGestureTriggered + + if ( + startedStep.to == KeyguardState.OCCLUDED && !transitioningFromPowerButtonGesture + ) { + // Set occluded upon STARTED, *unless* we're transitioning from the power + // button, in which case we're going to play an animation over the lockscreen UI + // and need to remain unoccluded until the transition finishes. + return@map OccludedState(occluded = true, animate = false) + } + + if ( + startedStep.from == KeyguardState.OCCLUDED && + startedStep.to == KeyguardState.LOCKSCREEN + ) { + // Set unoccluded immediately ONLY if we're transitioning back to the lockscreen + // since we need the views visible to animate them back down. This is a special + // case due to the way unocclusion remote animations are run. We can remove this + // once the unocclude animation uses the return animation framework. + return@map OccludedState(occluded = false, animate = false) + } + + // Otherwise, wait for the transition to FINISH to decide. + return@map null + } + .filterNotNull() + + /** Occlusion state to apply whenever a keyguard transition is FINISHED. */ + private val occlusionStateFromFinishedStep = + keyguardTransitionInteractor.finishedKeyguardTransitionStep + .sample(keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, ::Pair) + .map { (finishedStep, showWhenLockedOnTop) -> + // If we're FINISHED in OCCLUDED, we want to render as occluded. We also need to + // remain occluded if a SHOW_WHEN_LOCKED activity is on the top of the task stack, + // and we're in any state other than GONE. This is necessary, for example, when we + // transition from OCCLUDED to a bouncer state. Otherwise, we should not be + // occluded. + val occluded = + finishedStep.to == KeyguardState.OCCLUDED || + (showWhenLockedOnTop && finishedStep.to != KeyguardState.GONE) + OccludedState(occluded = occluded, animate = false) + } + + /** Occlusion state to apply to SKBVM's setOccluded call. */ + val keyguardViewOcclusionState = + merge(occlusionStateFromStartedStep, occlusionStateFromFinishedStep) + .distinctUntilChangedBy { + // Don't switch 'animate' values mid-transition. + it.occluded + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index f5237938ebfa..78fc1471053d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -42,8 +42,10 @@ import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel @@ -99,7 +101,9 @@ constructor( private val alternateBouncerToGoneTransitionViewModel: AlternateBouncerToGoneTransitionViewModel, private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, + private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel, private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel, + private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel, private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, private val glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel, @@ -155,8 +159,8 @@ constructor( .distinctUntilChanged() .dumpWhileCollecting("isShadeLocked") - private val shadeCollapseFadeInComplete = MutableStateFlow(false) - .dumpValue("shadeCollapseFadeInComplete") + private val shadeCollapseFadeInComplete = + MutableStateFlow(false).dumpValue("shadeCollapseFadeInComplete") val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> = interactor.configurationBasedDimensions @@ -187,9 +191,8 @@ constructor( statesForConstrainedNotifications.contains(it) }, keyguardTransitionInteractor - .transitionValue(LOCKSCREEN) - .onStart { emit(0f) } - .map { it > 0 } + .isInTransitionWhere { from, to -> from == LOCKSCREEN || to == LOCKSCREEN } + .onStart { emit(false) } ) { constrainedNotificationState, transitioningToOrFromLockscreen -> constrainedNotificationState || transitioningToOrFromLockscreen } @@ -362,16 +365,16 @@ constructor( private val alphaWhenGoneAndShadeState: Flow<Float> = combineTransform( - keyguardTransitionInteractor.transitions - .map { step -> step.to == GONE && step.transitionState == FINISHED } - .distinctUntilChanged(), - keyguardInteractor.statusBarState, - ) { isGoneTransitionFinished, statusBarState -> - if (isGoneTransitionFinished && statusBarState == SHADE) { - emit(1f) + keyguardTransitionInteractor.transitions + .map { step -> step.to == GONE && step.transitionState == FINISHED } + .distinctUntilChanged(), + keyguardInteractor.statusBarState, + ) { isGoneTransitionFinished, statusBarState -> + if (isGoneTransitionFinished && statusBarState == SHADE) { + emit(1f) + } } - } - .dumpWhileCollecting("alphaWhenGoneAndShadeState") + .dumpWhileCollecting("alphaWhenGoneAndShadeState") fun expansionAlpha(viewState: ViewStateAccessor): Flow<Float> { // All transition view models are mututally exclusive, and safe to merge @@ -379,7 +382,9 @@ constructor( merge( alternateBouncerToGoneTransitionViewModel.lockscreenAlpha, aodToLockscreenTransitionViewModel.notificationAlpha, + aodToOccludedTransitionViewModel.lockscreenAlpha(viewState), dozingToLockscreenTransitionViewModel.lockscreenAlpha, + dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState), dreamingToLockscreenTransitionViewModel.lockscreenAlpha, goneToAodTransitionViewModel.notificationAlpha, goneToDreamingTransitionViewModel.lockscreenAlpha, @@ -433,30 +438,35 @@ constructor( * alpha sources. */ val glanceableHubAlpha: Flow<Float> = - isOnGlanceableHubWithoutShade.flatMapLatest { isOnGlanceableHubWithoutShade -> - combineTransform( - lockscreenToGlanceableHubRunning, - glanceableHubToLockscreenRunning, - merge( - lockscreenToGlanceableHubTransitionViewModel.notificationAlpha, - glanceableHubToLockscreenTransitionViewModel.notificationAlpha, - ) - ) { lockscreenToGlanceableHubRunning, glanceableHubToLockscreenRunning, alpha -> - if (isOnGlanceableHubWithoutShade) { - // Notifications should not be visible on the glanceable hub. - // TODO(b/321075734): implement a way to actually set the notifications to gone - // while on the hub instead of just adjusting alpha - emit(0f) - } else if (lockscreenToGlanceableHubRunning || glanceableHubToLockscreenRunning) { - emit(alpha) - } else { - // Not on the hub and no transitions running, return full visibility so we don't - // block the notifications from showing. - emit(1f) + isOnGlanceableHubWithoutShade + .flatMapLatest { isOnGlanceableHubWithoutShade -> + combineTransform( + lockscreenToGlanceableHubRunning, + glanceableHubToLockscreenRunning, + merge( + lockscreenToGlanceableHubTransitionViewModel.notificationAlpha, + glanceableHubToLockscreenTransitionViewModel.notificationAlpha, + ) + ) { lockscreenToGlanceableHubRunning, glanceableHubToLockscreenRunning, alpha -> + if (isOnGlanceableHubWithoutShade) { + // Notifications should not be visible on the glanceable hub. + // TODO(b/321075734): implement a way to actually set the notifications to + // gone + // while on the hub instead of just adjusting alpha + emit(0f) + } else if ( + lockscreenToGlanceableHubRunning || glanceableHubToLockscreenRunning + ) { + emit(alpha) + } else { + // Not on the hub and no transitions running, return full visibility so we + // don't + // block the notifications from showing. + emit(1f) + } } } - } - .dumpWhileCollecting("glanceableHubAlpha") + .dumpWhileCollecting("glanceableHubAlpha") /** * Under certain scenarios, such as swiping up on the lockscreen, the container will need to be @@ -464,20 +474,20 @@ constructor( */ fun translationY(params: BurnInParameters): Flow<Float> { return combine( - aodBurnInViewModel.translationY(params).onStart { emit(0f) }, - isOnLockscreenWithoutShade, - merge( - keyguardInteractor.keyguardTranslationY, - occludedToLockscreenTransitionViewModel.lockscreenTranslationY, - ) - ) { burnInY, isOnLockscreenWithoutShade, translationY -> - if (isOnLockscreenWithoutShade) { - burnInY + translationY - } else { - 0f + aodBurnInViewModel.translationY(params).onStart { emit(0f) }, + isOnLockscreenWithoutShade, + merge( + keyguardInteractor.keyguardTranslationY, + occludedToLockscreenTransitionViewModel.lockscreenTranslationY, + ) + ) { burnInY, isOnLockscreenWithoutShade, translationY -> + if (isOnLockscreenWithoutShade) { + burnInY + translationY + } else { + 0f + } } - } - .dumpWhileCollecting("translationY") + .dumpWhileCollecting("translationY") } /** @@ -486,10 +496,10 @@ constructor( */ val translationX: Flow<Float> = merge( - lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX, - glanceableHubToLockscreenTransitionViewModel.notificationTranslationX, - ) - .dumpWhileCollecting("translationX") + lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX, + glanceableHubToLockscreenTransitionViewModel.notificationTranslationX, + ) + .dumpWhileCollecting("translationX") /** * When on keyguard, there is limited space to display notifications so calculate how many could diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index ee844345d2ec..ba89d4ac22cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -95,6 +95,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.domain.interactor.StatusBarKeyguardViewManagerInteractor; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.unfold.FoldAodAnimationController; @@ -351,8 +352,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor; private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor; private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor; - private final JavaAdapter mJavaAdapter; + private StatusBarKeyguardViewManagerInteractor mStatusBarKeyguardViewManagerInteractor; @Inject public StatusBarKeyguardViewManager( @@ -386,7 +387,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb SelectedUserInteractor selectedUserInteractor, Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor, JavaAdapter javaAdapter, - Lazy<SceneInteractor> sceneInteractorLazy + Lazy<SceneInteractor> sceneInteractorLazy, + StatusBarKeyguardViewManagerInteractor statusBarKeyguardViewManagerInteractor ) { mContext = context; mViewMediatorCallback = callback; @@ -421,6 +423,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mSurfaceBehindInteractor = surfaceBehindInteractor; mJavaAdapter = javaAdapter; mSceneInteractorLazy = sceneInteractorLazy; + mStatusBarKeyguardViewManagerInteractor = statusBarKeyguardViewManagerInteractor; } KeyguardTransitionInteractor mKeyguardTransitionInteractor; @@ -503,6 +506,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb lockscreenVis || animatingSurface ), this::consumeShowStatusBarKeyguardView); + + mJavaAdapter.alwaysCollectFlow( + mStatusBarKeyguardViewManagerInteractor.getKeyguardViewOcclusionState(), + (occlusionState) -> setOccluded( + occlusionState.getOccluded(), occlusionState.getAnimate())); } } @@ -1453,6 +1461,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb hideAlternateBouncer(false); executeAfterKeyguardGoneAction(); } + + if (KeyguardWmStateRefactor.isEnabled()) { + mKeyguardTransitionInteractor.startDismissKeyguardTransition(); + } } /** Display security message to relevant KeyguardMessageArea. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index 489665cd130a..51828c91de4b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -6,6 +6,7 @@ import android.app.WindowConfiguration import android.graphics.Point import android.graphics.Rect import android.os.PowerManager +import android.platform.test.annotations.DisableFlags import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.RemoteAnimationTarget @@ -15,6 +16,7 @@ import android.view.View import android.view.ViewRootImpl import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardViewController +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController @@ -130,6 +132,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { * surface, or the user will see the wallpaper briefly as the app animates in. */ @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun noSurfaceAnimation_ifWakeAndUnlocking() { whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true) @@ -320,6 +323,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { * If we are not wake and unlocking, we expect the unlock animation to play normally. */ @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun surfaceAnimation_multipleTargets() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( arrayOf(remoteTarget1, remoteTarget2), @@ -358,6 +362,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun surfaceBehindAlphaOverriddenTo0_ifNotInteractive() { whenever(powerManager.isInteractive).thenReturn(false) @@ -389,6 +394,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun surfaceBehindAlphaNotOverriddenTo0_ifInteractive() { whenever(powerManager.isInteractive).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 0957748c9938..0bd4cbec64dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -1263,7 +1263,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mSystemPropertiesHelper, () -> mock(WindowManagerLockscreenVisibilityManager.class), mSelectedUserInteractor, - mKeyguardInteractor); + mKeyguardInteractor, + mock(WindowManagerOcclusionManager.class)); mViewMediator.start(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt new file mode 100644 index 000000000000..7bef01a7a5ce --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2024 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. + */ + +/* + * Copyright (C) 2024 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.domain.interactor + +import android.os.PowerManager +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.testKosmos +import junit.framework.Assert.assertEquals +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.runner.RunWith +import org.mockito.Mockito.reset +import org.mockito.Mockito.spy + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class FromAodTransitionInteractorTest : SysuiTestCase() { + private val kosmos = + testKosmos().apply { + this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository()) + } + + private val testScope = kosmos.testScope + private val underTest = kosmos.fromAodTransitionInteractor + + private val powerInteractor = kosmos.powerInteractor + private val transitionRepository = kosmos.fakeKeyguardTransitionRepository + + @Before + fun setup() { + underTest.start() + + // Transition to AOD and set the power interactor asleep. + powerInteractor.setAsleepForTest() + runBlocking { + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope + ) + kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE) + reset(transitionRepository) + } + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToLockscreen_onWakeup() = + testScope.runTest { + powerInteractor.setAwakeForTest() + runCurrent() + + // Under default conditions, we should transition to LOCKSCREEN when waking up. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() = + testScope.runTest { + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) + powerInteractor.setAwakeForTest() + runCurrent() + + // Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.AOD, + to = KeyguardState.OCCLUDED, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() = + testScope.runTest { + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() = + testScope.runTest { + kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + testScope, + ) + runCurrent() + + // Make sure we're GONE. + assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + + // Get part way to AOD. + powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) + runCurrent() + + transitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING, + ) + + // Detect a power gesture and then wake up. + reset(transitionRepository) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + testScope, + ) + runCurrent() + + // Make sure we're GONE. + assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + + // Get all the way to AOD + powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) + transitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + testScope = testScope, + ) + + // Detect a power gesture and then wake up. + reset(transitionRepository) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should go to OCCLUDED - we came from GONE, but we finished in AOD, so this is no + // longer an insecure camera launch and it would be bad if we unlocked now. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, + testScope, + ) + runCurrent() + + // Make sure we're in LOCKSCREEN. + assertEquals( + KeyguardState.LOCKSCREEN, + kosmos.keyguardTransitionInteractor.getFinishedState() + ) + + // Get part way to AOD. + powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) + runCurrent() + + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING, + ) + + // Detect a power gesture and then wake up. + reset(transitionRepository) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testWakeAndUnlock_transitionsToGone_onlyAfterDismissCallPostWakeup() = + testScope.runTest { + kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK) + powerInteractor.setAwakeForTest() + runCurrent() + + // Waking up from wake and unlock should not start any transitions, we'll wait for the + // dismiss call. + assertThat(transitionRepository).noTransitionsStarted() + + underTest.dismissAod() + runCurrent() + + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.AOD, to = KeyguardState.GONE) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt new file mode 100644 index 000000000000..258dbf3efbae --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2024 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. + */ + +/* + * Copyright (C) 2024 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.domain.interactor + +import android.os.PowerManager +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.fakeCommunalRepository +import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.testKosmos +import junit.framework.Assert.assertEquals +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.runner.RunWith +import org.mockito.Mockito.reset +import org.mockito.Mockito.spy + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class FromDozingTransitionInteractorTest : SysuiTestCase() { + private val kosmos = + testKosmos().apply { + this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository()) + } + + private val testScope = kosmos.testScope + private val underTest = kosmos.fromDozingTransitionInteractor + + private val powerInteractor = kosmos.powerInteractor + private val transitionRepository = kosmos.fakeKeyguardTransitionRepository + + @Before + fun setup() { + underTest.start() + + // Transition to DOZING and set the power interactor asleep. + powerInteractor.setAsleepForTest() + runBlocking { + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + testScope + ) + kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE) + reset(transitionRepository) + } + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToLockscreen_onWakeup() = + testScope.runTest { + powerInteractor.setAwakeForTest() + runCurrent() + + // Under default conditions, we should transition to LOCKSCREEN when waking up. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() = + testScope.runTest { + kosmos.fakeCommunalRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal)) + ) + runCurrent() + + powerInteractor.setAwakeForTest() + runCurrent() + + // Under default conditions, we should transition to LOCKSCREEN when waking up. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DOZING, + to = KeyguardState.GLANCEABLE_HUB, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() = + testScope.runTest { + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) + powerInteractor.setAwakeForTest() + runCurrent() + + // Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DOZING, + to = KeyguardState.OCCLUDED, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop_evenIfIdleOnCommunal() = + testScope.runTest { + kosmos.fakeCommunalRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal)) + ) + runCurrent() + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) + powerInteractor.setAwakeForTest() + runCurrent() + + // Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED. + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DOZING, + to = KeyguardState.OCCLUDED, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() = + testScope.runTest { + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() = + testScope.runTest { + kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GONE) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.DOZING, + to = KeyguardState.GONE, + testScope, + ) + runCurrent() + + // Make sure we're GONE. + assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + + // Get part way to AOD. + powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) + runCurrent() + + transitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.DOZING, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING, + ) + + // Detect a power gesture and then wake up. + reset(transitionRepository) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GONE) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.DOZING, + to = KeyguardState.GONE, + testScope, + ) + runCurrent() + + // Make sure we're GONE. + assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState()) + + // Get all the way to AOD + powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) + transitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.DOZING, + testScope = testScope, + ) + + // Detect a power gesture and then wake up. + reset(transitionRepository) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should go to OCCLUDED - we came from GONE, but we finished in AOD, so this is no + // longer an insecure camera launch and it would be bad if we unlocked now. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.DOZING, + to = KeyguardState.LOCKSCREEN, + testScope, + ) + runCurrent() + + // Make sure we're in LOCKSCREEN. + assertEquals( + KeyguardState.LOCKSCREEN, + kosmos.keyguardTransitionInteractor.getFinishedState() + ) + + // Get part way to AOD. + powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN) + runCurrent() + + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING, + ) + + // Detect a power gesture and then wake up. + reset(transitionRepository) + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + // We should head back to GONE since we started there. + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.OCCLUDED) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt new file mode 100644 index 000000000000..f534ba5bc68c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2024 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. + */ + +/* + * Copyright (C) 2024 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.domain.interactor + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.testKosmos +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.runner.RunWith +import org.mockito.Mockito.reset +import org.mockito.Mockito.spy + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class FromDreamingTransitionInteractorTest : SysuiTestCase() { + private val kosmos = + testKosmos().apply { + this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository()) + } + + private val testScope = kosmos.testScope + private val underTest = kosmos.fromDreamingTransitionInteractor + + private val powerInteractor = kosmos.powerInteractor + private val transitionRepository = kosmos.fakeKeyguardTransitionRepository + + @Before + fun setup() { + underTest.start() + + // Transition to DOZING and set the power interactor asleep. + powerInteractor.setAsleepForTest() + runBlocking { + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + testScope + ) + kosmos.keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE) + reset(transitionRepository) + } + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_ifDreamEnds_occludingActivityOnTop() = + testScope.runTest { + kosmos.fakeKeyguardRepository.setDreaming(true) + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + testScope, + ) + runCurrent() + + reset(transitionRepository) + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true) + kosmos.fakeKeyguardRepository.setDreaming(false) + runCurrent() + + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DREAMING, + to = KeyguardState.OCCLUDED, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testDoesNotTransitionToOccluded_occludingActivityOnTop_whileStillDreaming() = + testScope.runTest { + kosmos.fakeKeyguardRepository.setDreaming(true) + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + testScope, + ) + runCurrent() + + reset(transitionRepository) + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true) + runCurrent() + + assertThat(transitionRepository).noTransitionsStarted() + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionsToLockscreen_whenOccludingActivityEnds() = + testScope.runTest { + kosmos.fakeKeyguardRepository.setDreaming(true) + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true) + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + testScope, + ) + runCurrent() + + reset(transitionRepository) + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false) + runCurrent() + + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.DREAMING, + to = KeyguardState.LOCKSCREEN, + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt index 6aebe365dc8c..c3e24d579491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt @@ -16,6 +16,8 @@ package com.android.systemui.keyguard.domain.interactor +import android.app.ActivityManager +import android.app.WindowConfiguration import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -26,6 +28,7 @@ import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -42,6 +45,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.spy import org.mockito.Mockito.verify @@ -171,4 +175,49 @@ class FromLockscreenTransitionInteractorTest : SysuiTestCase() { assertThatRepository(transitionRepository).noTransitionsStarted() } + + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionsToOccluded_whenShowWhenLockedActivityOnTop() = + testScope.runTest { + underTest.start() + runCurrent() + + reset(transitionRepository) + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo( + true, + ActivityManager.RunningTaskInfo().apply { + topActivityType = WindowConfiguration.ACTIVITY_TYPE_STANDARD + } + ) + runCurrent() + + assertThatRepository(transitionRepository) + .startedTransition( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.OCCLUDED, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionsToDream_whenDreamActivityOnTop() = + testScope.runTest { + underTest.start() + runCurrent() + + reset(transitionRepository) + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo( + true, + ActivityManager.RunningTaskInfo().apply { + topActivityType = WindowConfiguration.ACTIVITY_TYPE_DREAM + } + ) + runCurrent() + + assertThatRepository(transitionRepository) + .startedTransition( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt new file mode 100644 index 000000000000..d3c48483d100 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2024 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. + */ + +/* + * Copyright (C) 2024 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.domain.interactor + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.fakeCommunalRepository +import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.testKosmos +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.runner.RunWith +import org.mockito.Mockito.reset +import org.mockito.Mockito.spy + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class FromOccludedTransitionInteractorTest : SysuiTestCase() { + private val kosmos = + testKosmos().apply { + this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository()) + } + + private val testScope = kosmos.testScope + private val underTest = kosmos.fromOccludedTransitionInteractor + + private val powerInteractor = kosmos.powerInteractor + private val transitionRepository = kosmos.fakeKeyguardTransitionRepository + + @Before + fun setup() { + underTest.start() + + // Transition to OCCLUDED and set up PowerInteractor and the occlusion repository. + powerInteractor.setAwakeForTest() + runBlocking { + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true) + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.OCCLUDED, + testScope + ) + reset(transitionRepository) + } + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testShowWhenLockedActivity_noLongerOnTop_transitionsToLockscreen() = + testScope.runTest { + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false) + runCurrent() + + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.OCCLUDED, + to = KeyguardState.LOCKSCREEN, + ) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testShowWhenLockedActivity_noLongerOnTop_transitionsToGlanceableHub_ifIdleOnCommunal() = + testScope.runTest { + kosmos.fakeCommunalRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal)) + ) + runCurrent() + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false) + runCurrent() + + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.OCCLUDED, + to = KeyguardState.GLANCEABLE_HUB, + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt index f33a5c9484ee..7ee8963aaa15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt @@ -16,14 +16,23 @@ package com.android.systemui.keyguard.domain.interactor +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository +import com.android.systemui.communal.data.repository.fakeCommunalRepository +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.user.domain.interactor.selectedUserInteractor @@ -31,20 +40,27 @@ import junit.framework.Assert.assertEquals import junit.framework.Assert.assertTrue import junit.framework.Assert.fail import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.reset +import org.mockito.Mockito.spy @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class FromPrimaryBouncerTransitionInteractorTest : SysuiTestCase() { - val kosmos = testKosmos() + val kosmos = + testKosmos().apply { + this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository()) + } val underTest = kosmos.fromPrimaryBouncerTransitionInteractor val testScope = kosmos.testScope val selectedUserInteractor = kosmos.selectedUserInteractor val transitionRepository = kosmos.fakeKeyguardTransitionRepository + val bouncerRepository = kosmos.fakeKeyguardBouncerRepository @Test fun testSurfaceBehindVisibility() = @@ -193,4 +209,85 @@ class FromPrimaryBouncerTransitionInteractorTest : SysuiTestCase() { fail("surfaceBehindModel was unexpectedly null.") } } + + @Test + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testReturnToLockscreen_whenBouncerHides() = + testScope.runTest { + underTest.start() + bouncerRepository.setPrimaryShow(true) + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.PRIMARY_BOUNCER, + testScope + ) + + reset(transitionRepository) + + bouncerRepository.setPrimaryShow(false) + runCurrent() + + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.LOCKSCREEN + ) + } + + @Test + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testReturnToGlanceableHub_whenBouncerHides_ifIdleOnCommunal() = + testScope.runTest { + underTest.start() + kosmos.fakeCommunalRepository.setTransitionState( + flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal)) + ) + bouncerRepository.setPrimaryShow(true) + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.PRIMARY_BOUNCER, + testScope + ) + + reset(transitionRepository) + + bouncerRepository.setPrimaryShow(false) + runCurrent() + + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.GLANCEABLE_HUB + ) + } + + @Test + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToOccluded_bouncerHide_occludingActivityOnTop() = + testScope.runTest { + underTest.start() + bouncerRepository.setPrimaryShow(true) + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.PRIMARY_BOUNCER, + testScope + ) + + reset(transitionRepository) + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) + runCurrent() + + // Shouldn't transition to OCCLUDED until the bouncer hides. + assertThat(transitionRepository).noTransitionsStarted() + + bouncerRepository.setPrimaryShow(false) + runCurrent() + + assertThat(transitionRepository) + .startedTransition( + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.OCCLUDED + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt new file mode 100644 index 000000000000..8a77ed2130a9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2024 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. + */ + +/* + * Copyright (C) 2024 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.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest +import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import kotlin.test.Test +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class KeyguardOcclusionInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val underTest = kosmos.keyguardOcclusionInteractor + private val powerInteractor = kosmos.powerInteractor + private val transitionRepository = kosmos.fakeKeyguardTransitionRepository + + @Test + fun testTransitionFromPowerGesture_whileGoingToSleep_isTrue() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING + ) + + powerInteractor.onCameraLaunchGestureDetected() + runCurrent() + + assertTrue(underTest.shouldTransitionFromPowerButtonGesture()) + } + + @Test + fun testTransitionFromPowerGesture_whileAsleep_isTrue() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + ) + + powerInteractor.onCameraLaunchGestureDetected() + runCurrent() + + assertTrue(underTest.shouldTransitionFromPowerButtonGesture()) + } + + @Test + fun testTransitionFromPowerGesture_whileWaking_isFalse() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + ) + transitionRepository.sendTransitionSteps( + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING + ) + + powerInteractor.onCameraLaunchGestureDetected() + runCurrent() + + assertFalse(underTest.shouldTransitionFromPowerButtonGesture()) + } + + @Test + fun testTransitionFromPowerGesture_whileAwake_isFalse() = + testScope.runTest { + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + ) + transitionRepository.sendTransitionSteps( + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, + testScope = testScope, + ) + + powerInteractor.onCameraLaunchGestureDetected() + runCurrent() + + assertFalse(underTest.shouldTransitionFromPowerButtonGesture()) + } + + @Test + fun testShowWhenLockedActivityLaunchedFromPowerGesture_notTrueSecondTime() = + testScope.runTest { + val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture) + powerInteractor.setAsleepForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + testScope = testScope, + ) + + powerInteractor.onCameraLaunchGestureDetected() + powerInteractor.setAwakeForTest() + runCurrent() + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) + runCurrent() + + assertThat(values) + .containsExactly( + false, + true, + ) + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false) + runCurrent() + + assertThat(values) + .containsExactly( + false, + true, + false, + ) + + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) + runCurrent() + + assertThat(values) + .containsExactly( + false, + true, + // Power button gesture was not triggered a second time, so this should remain + // false. + false, + ) + } + + @Test + fun testShowWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone() = + testScope.runTest { + val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture) + powerInteractor.setAwakeForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + testScope = testScope, + ) + + powerInteractor.setAsleepForTest() + transitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + testScope = testScope, + throughTransitionState = TransitionState.RUNNING + ) + + powerInteractor.onCameraLaunchGestureDetected() + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) + powerInteractor.setAwakeForTest() + runCurrent() + + transitionRepository.sendTransitionSteps( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + testScope = testScope, + ) + + assertThat(values) + .containsExactly( + false, + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 92aad692ac52..95606ae81e5c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -21,8 +21,8 @@ import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN +import com.android.systemui.Flags import com.android.systemui.Flags.FLAG_COMMUNAL_HUB -import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.communal.domain.interactor.communalInteractor @@ -40,7 +40,6 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat -import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest @@ -48,7 +47,6 @@ import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.statusbar.commandQueue import com.android.systemui.testKosmos -import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -92,30 +90,26 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { private var commandQueue = kosmos.fakeCommandQueue private val shadeRepository = kosmos.fakeShadeRepository private val transitionRepository = kosmos.fakeKeyguardTransitionRepository - private val transitionInteractor = kosmos.keyguardTransitionInteractor private lateinit var featureFlags: FakeFeatureFlags // Used to verify transition requests for test output @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel - @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor private val fromLockscreenTransitionInteractor = kosmos.fromLockscreenTransitionInteractor - private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor - private lateinit var fromDozingTransitionInteractor: FromDozingTransitionInteractor - private lateinit var fromOccludedTransitionInteractor: FromOccludedTransitionInteractor - private lateinit var fromGoneTransitionInteractor: FromGoneTransitionInteractor - private lateinit var fromAodTransitionInteractor: FromAodTransitionInteractor - private lateinit var fromAlternateBouncerTransitionInteractor: - FromAlternateBouncerTransitionInteractor + private val fromDreamingTransitionInteractor = kosmos.fromDreamingTransitionInteractor + private val fromDozingTransitionInteractor = kosmos.fromDozingTransitionInteractor + private val fromOccludedTransitionInteractor = kosmos.fromOccludedTransitionInteractor + private val fromGoneTransitionInteractor = kosmos.fromGoneTransitionInteractor + private val fromAodTransitionInteractor = kosmos.fromAodTransitionInteractor + private val fromAlternateBouncerTransitionInteractor = + kosmos.fromAlternateBouncerTransitionInteractor private val fromPrimaryBouncerTransitionInteractor = kosmos.fromPrimaryBouncerTransitionInteractor - private lateinit var fromDreamingLockscreenHostedTransitionInteractor: - FromDreamingLockscreenHostedTransitionInteractor - private lateinit var fromGlanceableHubTransitionInteractor: - FromGlanceableHubTransitionInteractor + private val fromDreamingLockscreenHostedTransitionInteractor = + kosmos.fromDreamingLockscreenHostedTransitionInteractor + private val fromGlanceableHubTransitionInteractor = kosmos.fromGlanceableHubTransitionInteractor private val powerInteractor = kosmos.powerInteractor - private val keyguardInteractor = kosmos.keyguardInteractor private val communalInteractor = kosmos.communalInteractor @Before @@ -125,122 +119,21 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN) mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) + mSetFlagsRule.disableFlags( + Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR, + ) featureFlags = FakeFeatureFlags() - val glanceableHubTransitions = - GlanceableHubTransitions( - bgDispatcher = kosmos.testDispatcher, - transitionInteractor = transitionInteractor, - transitionRepository = transitionRepository, - communalInteractor = communalInteractor - ) - fromLockscreenTransitionInteractor.start() fromPrimaryBouncerTransitionInteractor.start() - - fromDreamingTransitionInteractor = - FromDreamingTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - glanceableHubTransitions = glanceableHubTransitions, - ) - .apply { start() } - - fromDreamingLockscreenHostedTransitionInteractor = - FromDreamingLockscreenHostedTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - ) - .apply { start() } - - fromAodTransitionInteractor = - FromAodTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - powerInteractor = powerInteractor, - ) - .apply { start() } - - fromGoneTransitionInteractor = - FromGoneTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - powerInteractor = powerInteractor, - communalInteractor = communalInteractor, - ) - .apply { start() } - - fromDozingTransitionInteractor = - FromDozingTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - powerInteractor = powerInteractor, - communalInteractor = communalInteractor, - ) - .apply { start() } - - fromOccludedTransitionInteractor = - FromOccludedTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - powerInteractor = powerInteractor, - communalInteractor = communalInteractor, - ) - .apply { start() } - - fromAlternateBouncerTransitionInteractor = - FromAlternateBouncerTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - communalInteractor = communalInteractor, - powerInteractor = powerInteractor, - ) - .apply { start() } - - fromGlanceableHubTransitionInteractor = - FromGlanceableHubTransitionInteractor( - scope = testScope, - bgDispatcher = kosmos.testDispatcher, - mainDispatcher = kosmos.testDispatcher, - glanceableHubTransitions = glanceableHubTransitions, - keyguardInteractor = keyguardInteractor, - transitionRepository = transitionRepository, - transitionInteractor = transitionInteractor, - powerInteractor = powerInteractor, - ) - .apply { start() } - - mSetFlagsRule.disableFlags( - FLAG_KEYGUARD_WM_STATE_REFACTOR, - ) + fromDreamingTransitionInteractor.start() + fromDreamingLockscreenHostedTransitionInteractor.start() + fromAodTransitionInteractor.start() + fromGoneTransitionInteractor.start() + fromDozingTransitionInteractor.start() + fromOccludedTransitionInteractor.start() + fromAlternateBouncerTransitionInteractor.start() + fromGlanceableHubTransitionInteractor.start() } @Test @@ -257,7 +150,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { .startedTransition( to = KeyguardState.PRIMARY_BOUNCER, from = KeyguardState.LOCKSCREEN, - ownerName = "FromLockscreenTransitionInteractor", + ownerName = + "FromLockscreenTransitionInteractor" + + "(#listenForLockscreenToPrimaryBouncer)", animatorAssertion = { it.isNotNull() } ) @@ -282,7 +177,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { .startedTransition( to = KeyguardState.DOZING, from = KeyguardState.OCCLUDED, - ownerName = "FromOccludedTransitionInteractor", + ownerName = "FromOccludedTransitionInteractor(Sleep transition triggered)", animatorAssertion = { it.isNotNull() } ) @@ -307,7 +202,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { .startedTransition( to = KeyguardState.AOD, from = KeyguardState.OCCLUDED, - ownerName = "FromOccludedTransitionInteractor", + ownerName = "FromOccludedTransitionInteractor(Sleep transition triggered)", animatorAssertion = { it.isNotNull() } ) @@ -389,7 +284,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { .startedTransition( to = KeyguardState.DOZING, from = KeyguardState.LOCKSCREEN, - ownerName = "FromLockscreenTransitionInteractor", + ownerName = "FromLockscreenTransitionInteractor(Sleep transition triggered)", animatorAssertion = { it.isNotNull() } ) @@ -414,7 +309,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { .startedTransition( to = KeyguardState.AOD, from = KeyguardState.LOCKSCREEN, - ownerName = "FromLockscreenTransitionInteractor", + ownerName = "FromLockscreenTransitionInteractor(Sleep transition triggered)", animatorAssertion = { it.isNotNull() } ) @@ -778,7 +673,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { .startedTransition( to = KeyguardState.DOZING, from = KeyguardState.GONE, - ownerName = "FromGoneTransitionInteractor", + ownerName = "FromGoneTransitionInteractor(Sleep transition triggered)", animatorAssertion = { it.isNotNull() } ) @@ -803,7 +698,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { .startedTransition( to = KeyguardState.AOD, from = KeyguardState.GONE, - ownerName = "FromGoneTransitionInteractor", + ownerName = "FromGoneTransitionInteractor(Sleep transition triggered)", animatorAssertion = { it.isNotNull() } ) @@ -1070,12 +965,14 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { @Test fun primaryBouncerToAod() = testScope.runTest { + // GIVEN aod available + keyguardRepository.setAodAvailable(true) + runCurrent() + // GIVEN a prior transition has run to PRIMARY_BOUNCER bouncerRepository.setPrimaryShow(true) runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER) - // GIVEN aod available and starting to sleep - keyguardRepository.setAodAvailable(true) powerInteractor.setAsleepForTest() // WHEN the primaryBouncer stops showing @@ -1085,7 +982,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // THEN a transition to AOD should occur assertThat(transitionRepository) .startedTransition( - ownerName = "FromPrimaryBouncerTransitionInteractor", + ownerName = + "FromPrimaryBouncerTransitionInteractor" + "(Sleep transition triggered)", from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.AOD, animatorAssertion = { it.isNotNull() }, @@ -1112,7 +1010,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // THEN a transition to DOZING should occur assertThat(transitionRepository) .startedTransition( - ownerName = "FromPrimaryBouncerTransitionInteractor", + ownerName = + "FromPrimaryBouncerTransitionInteractor" + "(Sleep transition triggered)", from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.DOZING, animatorAssertion = { it.isNotNull() }, @@ -1642,7 +1541,9 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur assertThat(transitionRepository) .startedTransition( - ownerName = "FromLockscreenTransitionInteractor", + ownerName = + "FromLockscreenTransitionInteractor" + + "(#listenForLockscreenToPrimaryBouncerDragging)", from = KeyguardState.LOCKSCREEN, to = KeyguardState.PRIMARY_BOUNCER, animatorAssertion = { it.isNull() }, // dragging should be manually animated diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt index b3fc25c47912..24195fe0640c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import android.platform.test.annotations.DisableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION import com.android.systemui.SysUITestComponent import com.android.systemui.SysUITestModule @@ -238,6 +240,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun animationsEnabled_isTrue_whenKeyguardIsShowing() = testComponent.runTest { keyguardTransitionRepository.sendTransitionStep( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index f050857d5df2..562aa6a4f497 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -95,6 +95,7 @@ import com.android.systemui.shade.ShadeLockscreenInteractor; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.domain.interactor.StatusBarKeyguardViewManagerInteractor; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; @@ -226,7 +227,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mSelectedUserInteractor, () -> mock(KeyguardSurfaceBehindInteractor.class), mock(JavaAdapter.class), - () -> mock(SceneInteractor.class)) { + () -> mock(SceneInteractor.class), + mock(StatusBarKeyguardViewManagerInteractor.class)) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; @@ -736,7 +738,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mSelectedUserInteractor, () -> mock(KeyguardSurfaceBehindInteractor.class), mock(JavaAdapter.class), - () -> mock(SceneInteractor.class)) { + () -> mock(SceneInteractor.class), + mock(StatusBarKeyguardViewManagerInteractor.class)) { @Override public ViewRootImpl getViewRootImpl() { return mViewRootImpl; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt index c3f677e8d0d3..d5411ad77ce4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.plugins.activityStarter @@ -40,5 +41,6 @@ val Kosmos.occludingAppDeviceEntryInteractor by context = mockedContext, activityStarter = activityStarter, powerInteractor = powerInteractor, + keyguardTransitionInteractor = keyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index a9a2d91c0815..dcbd5777489a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import android.annotation.FloatRange +import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo @@ -65,55 +66,79 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio } /** - * Sends STARTED, RUNNING, and FINISHED TransitionSteps between [from] and [to], calling - * [runCurrent] after each step. + * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step. + * + * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part + * way using [throughTransitionState]. */ suspend fun sendTransitionSteps( from: KeyguardState, to: KeyguardState, testScope: TestScope, + throughTransitionState: TransitionState = TransitionState.FINISHED, ) { - sendTransitionSteps(from, to, testScope.testScheduler) + sendTransitionSteps(from, to, testScope.testScheduler, throughTransitionState) } /** - * Sends STARTED, RUNNING, and FINISHED TransitionSteps between [from] and [to], calling - * [runCurrent] after each step. + * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step. + * + * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part + * way using [throughTransitionState]. */ suspend fun sendTransitionSteps( from: KeyguardState, to: KeyguardState, testScheduler: TestCoroutineScheduler, + throughTransitionState: TransitionState = TransitionState.FINISHED, ) { sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, - from = from, - to = to, - value = 0f, - ) + step = + TransitionStep( + transitionState = TransitionState.STARTED, + from = from, + to = to, + value = 0f, + ) ) testScheduler.runCurrent() - sendTransitionStep( - TransitionStep( - transitionState = TransitionState.RUNNING, - from = from, - to = to, - value = 0.5f + if ( + throughTransitionState == TransitionState.RUNNING || + throughTransitionState == TransitionState.FINISHED + ) { + sendTransitionStep( + step = + TransitionStep( + transitionState = TransitionState.RUNNING, + from = from, + to = to, + value = 0.5f + ) ) - ) - testScheduler.runCurrent() + testScheduler.runCurrent() + } - sendTransitionStep( - TransitionStep( - transitionState = TransitionState.FINISHED, - from = from, - to = to, - value = 1f, + if (throughTransitionState == TransitionState.FINISHED) { + sendTransitionStep( + step = + TransitionStep( + transitionState = TransitionState.FINISHED, + from = from, + to = to, + value = 1f, + ) ) + testScheduler.runCurrent() + } + } + + suspend fun sendTransitionStep(step: TransitionStep, validateStep: Boolean = true) { + this.sendTransitionStep( + step = step, + validateStep = validateStep, + ownerName = step.ownerName ) - testScheduler.runCurrent() } /** @@ -132,7 +157,22 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio * If you're testing something involving transitions themselves and are sure you want to send * only a FINISHED step, override [validateStep]. */ - suspend fun sendTransitionStep(step: TransitionStep, validateStep: Boolean = true) { + suspend fun sendTransitionStep( + from: KeyguardState = KeyguardState.OFF, + to: KeyguardState = KeyguardState.OFF, + value: Float = 0f, + transitionState: TransitionState = TransitionState.FINISHED, + ownerName: String = "", + step: TransitionStep = + TransitionStep( + from = from, + to = to, + value = value, + transitionState = transitionState, + ownerName = ownerName + ), + validateStep: Boolean = true + ) { _transitions.replayCache.last().let { lastStep -> if ( validateStep && @@ -159,7 +199,9 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio step: TransitionStep, validateStep: Boolean = true ): Job { - return coroutineScope.launch { sendTransitionStep(step, validateStep) } + return coroutineScope.launch { + sendTransitionStep(step = step, validateStep = validateStep) + } } suspend fun sendTransitionSteps( @@ -168,12 +210,13 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio validateStep: Boolean = true ) { steps.forEach { - sendTransitionStep(it, validateStep = validateStep) + sendTransitionStep(step = it, validateStep = validateStep) testScope.testScheduler.runCurrent() } } override fun startTransition(info: TransitionInfo): UUID? { + Log.i("TEST", "Start transition: ", Exception()) return if (info.animator == null) UUID.randomUUID() else null } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepositoryKosmos.kt new file mode 100644 index 000000000000..4c8bf9054106 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardOcclusionRepositoryKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 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.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.keyguardOcclusionRepository by Kosmos.Fixture { KeyguardOcclusionRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..530cbedbdd0c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 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.domain.interactor + +import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor + +val Kosmos.fromAlternateBouncerTransitionInteractor by + Kosmos.Fixture { + FromAlternateBouncerTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, + keyguardInteractor = keyguardInteractor, + communalInteractor = communalInteractor, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt index 2477415cc06e..bbe37c18dd08 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt @@ -18,19 +18,21 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor val Kosmos.fromAodTransitionInteractor by Kosmos.Fixture { FromAodTransitionInteractor( transitionRepository = fakeKeyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, - scope = testScope, + scope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..23dcd965c028 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 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.domain.interactor + +import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor + +var Kosmos.fromDozingTransitionInteractor by + Kosmos.Fixture { + FromDozingTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, + keyguardInteractor = keyguardInteractor, + communalInteractor = communalInteractor, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..f7a9d59eac26 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 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.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor + +var Kosmos.fromDreamingLockscreenHostedTransitionInteractor by + Kosmos.Fixture { + FromDreamingLockscreenHostedTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, + keyguardInteractor = keyguardInteractor, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..135644cfac3e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 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.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor + +var Kosmos.fromDreamingTransitionInteractor by + Kosmos.Fixture { + FromDreamingTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, + keyguardInteractor = keyguardInteractor, + glanceableHubTransitions = glanceableHubTransitions, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..1695327d75bc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 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.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor + +var Kosmos.fromGlanceableHubTransitionInteractor by + Kosmos.Fixture { + FromGlanceableHubTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, + keyguardInteractor = keyguardInteractor, + powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, + glanceableHubTransitions = glanceableHubTransitions, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt index 25fc67a9691b..604d9e435e8e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt @@ -17,11 +17,13 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor val Kosmos.fromGoneTransitionInteractor by Kosmos.Fixture { @@ -34,5 +36,7 @@ val Kosmos.fromGoneTransitionInteractor by keyguardInteractor = keyguardInteractor, powerInteractor = powerInteractor, communalInteractor = communalInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, + biometricSettingsRepository = biometricSettingsRepository, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt index 3b52676fc0ef..162fd9029bb0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -23,6 +23,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor var Kosmos.fromLockscreenTransitionInteractor by Kosmos.Fixture { @@ -38,5 +39,6 @@ var Kosmos.fromLockscreenTransitionInteractor by powerInteractor = powerInteractor, glanceableHubTransitions = glanceableHubTransitions, swipeToDismissInteractor = swipeToDismissInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..fc740a180dc4 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 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.domain.interactor + +import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor + +val Kosmos.fromOccludedTransitionInteractor by + Kosmos.Fixture { + FromOccludedTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, + keyguardInteractor = keyguardInteractor, + powerInteractor = powerInteractor, + communalInteractor = communalInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt index 6b764491f32a..98babffb50d3 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -24,6 +24,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.user.domain.interactor.selectedUserInteractor var Kosmos.fromPrimaryBouncerTransitionInteractor by @@ -40,5 +41,6 @@ var Kosmos.fromPrimaryBouncerTransitionInteractor by keyguardSecurityModel = keyguardSecurityModel, selectedUserInteractor = selectedUserInteractor, powerInteractor = powerInteractor, + keyguardOcclusionInteractor = keyguardOcclusionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt index 6df7493be200..6cc1e8eba73d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt @@ -16,19 +16,21 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope -import dagger.Lazy val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by Kosmos.Fixture { KeyguardTransitionInteractor( scope = applicationCoroutineScope, repository = keyguardTransitionRepository, - fromLockscreenTransitionInteractor = Lazy { fromLockscreenTransitionInteractor }, - fromPrimaryBouncerTransitionInteractor = - Lazy { fromPrimaryBouncerTransitionInteractor }, - fromAodTransitionInteractor = Lazy { fromAodTransitionInteractor }, + keyguardRepository = keyguardRepository, + fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor }, + fromPrimaryBouncerTransitionInteractor = { fromPrimaryBouncerTransitionInteractor }, + fromAodTransitionInteractor = { fromAodTransitionInteractor }, + fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor }, + fromDozingTransitionInteractor = { fromDozingTransitionInteractor }, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModelKosmos.kt new file mode 100644 index 000000000000..8162520e5d88 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModelKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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.ui.viewmodel + +import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow +import com.android.systemui.kosmos.Kosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi + +@ExperimentalCoroutinesApi +val Kosmos.dozingToOccludedTransitionViewModel by + Kosmos.Fixture { + DozingToOccludedTransitionViewModel( + animationFlow = keyguardTransitionAnimationFlow, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt index c2300a1ed1ad..75e3ac24e381 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.ui.viewmodel @@ -41,8 +40,10 @@ val Kosmos.keyguardRootViewModel by Fixture { alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel, aodToGoneTransitionViewModel = aodToGoneTransitionViewModel, aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, + aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel, dozingToGoneTransitionViewModel = dozingToGoneTransitionViewModel, dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel, + dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel, dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel, glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel, goneToAodTransitionViewModel = goneToAodTransitionViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt new file mode 100644 index 000000000000..d79374021968 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 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.statusbar.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.domain.interactor.powerInteractor + +val Kosmos.keyguardOcclusionInteractor by + Kosmos.Fixture { + KeyguardOcclusionInteractor( + scope = testScope.backgroundScope, + repository = keyguardOcclusionRepository, + powerInteractor = powerInteractor, + transitionInteractor = keyguardTransitionInteractor, + keyguardInteractor = keyguardInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt new file mode 100644 index 000000000000..9e34fe8d7c61 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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.statusbar.domain.interactor + +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.power.domain.interactor.powerInteractor + +val Kosmos.statusBarKeyguardViewManagerInteractor by + Kosmos.Fixture { + StatusBarKeyguardViewManagerInteractor( + keyguardTransitionInteractor = this.keyguardTransitionInteractor, + keyguardOcclusionInteractor = this.keyguardOcclusionInteractor, + powerInteractor = this.powerInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt index 106e85cc8d85..c01366489c69 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt @@ -23,7 +23,9 @@ import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInterac import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.aodToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.dozingToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel @@ -57,7 +59,9 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { communalInteractor = communalInteractor, alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel, aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, + aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel, dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel, + dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel, dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel, goneToAodTransitionViewModel = goneToAodTransitionViewModel, goneToDozingTransitionViewModel = goneToDozingTransitionViewModel, |