diff options
7 files changed, 320 insertions, 16 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index ade417d7bf5c..64fcef51755d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -125,6 +125,7 @@ import com.android.systemui.charging.WiredChargingRippleController; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -245,6 +246,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Executor; +import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; @@ -551,6 +553,25 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final WakefulnessLifecycle mWakefulnessLifecycle; protected final PowerInteractor mPowerInteractor; + private final CommunalInteractor mCommunalInteractor; + + /** + * True if the device is showing the glanceable hub. See + * {@link CommunalInteractor#isIdleOnCommunal()} for more details. + */ + private boolean mIsIdleOnCommunal = false; + private final Consumer<Boolean> mIdleOnCommunalConsumer = (Boolean idleOnCommunal) -> { + if (idleOnCommunal == mIsIdleOnCommunal) { + // Ignore initial value coming through the flow. + return; + } + + mIsIdleOnCommunal = idleOnCommunal; + // Trigger an update for the scrim state when we enter or exit glanceable hub, so that we + // can transition to/from ScrimState.GLANCEABLE_HUB if needed. + updateScrimController(); + }; + private boolean mNoAnimationOnNextBarModeChange; private final SysuiStatusBarStateController mStatusBarStateController; @@ -618,6 +639,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { ScreenLifecycle screenLifecycle, WakefulnessLifecycle wakefulnessLifecycle, PowerInteractor powerInteractor, + CommunalInteractor communalInteractor, SysuiStatusBarStateController statusBarStateController, Optional<Bubbles> bubblesOptional, Lazy<NoteTaskController> noteTaskControllerLazy, @@ -722,6 +744,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScreenLifecycle = screenLifecycle; mWakefulnessLifecycle = wakefulnessLifecycle; mPowerInteractor = powerInteractor; + mCommunalInteractor = communalInteractor; mStatusBarStateController = statusBarStateController; mBubblesOptional = bubblesOptional; mNoteTaskControllerLazy = noteTaskControllerLazy; @@ -1051,6 +1074,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { //TODO(b/264502026) move the rest of the listeners here. mDeviceStateManager.registerCallback(mMainExecutor, new FoldStateListener(mContext, this::onFoldedStateChanged)); + + mJavaAdapter.alwaysCollectFlow( + mCommunalInteractor.isIdleOnCommunal(), + mIdleOnCommunalConsumer); } /** @@ -2795,6 +2822,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // This will cancel the keyguardFadingAway animation if it is running. We need to do // this as otherwise it can remain pending and leave keyguard in a weird state. mUnlockScrimCallback.onCancelled(); + } else if (mIsIdleOnCommunal) { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); } else if (mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded() && !unlocking) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 3f20eaf45260..6f78604e996f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER; +import static com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB; import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE; +import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN; import static com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -62,6 +64,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.ScrimAlpha; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -292,6 +295,30 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimBehind.setViewAlpha(mBehindAlpha); }; + /** + * Consumer that fades the behind scrim in and out during the transition between the lock screen + * and the glanceable hub. + * + * While the lock screen is showing, the behind scrim is used to slightly darken the lock screen + * wallpaper underneath. Since the glanceable hub is under all of the scrims, we want to fade + * out the scrim so that the glanceable hub isn't darkened when it opens. + * + * {@link #applyState()} handles the scrim alphas once on the glanceable hub, this is only + * responsible for setting the behind alpha during the transition. + */ + private final Consumer<TransitionStep> mGlanceableHubConsumer = (TransitionStep step) -> { + final float baseAlpha = ScrimState.KEYGUARD.getBehindAlpha(); + final float transitionProgress = step.getValue(); + if (step.getTo() == KeyguardState.LOCKSCREEN) { + // Transitioning back to lock screen, fade in behind scrim again. + mBehindAlpha = baseAlpha * transitionProgress; + } else if (step.getTo() == GLANCEABLE_HUB) { + // Transitioning to glanceable hub, fade out behind scrim. + mBehindAlpha = baseAlpha * (1 - transitionProgress); + } + mScrimBehind.setViewAlpha(mBehindAlpha); + }; + Consumer<TransitionStep> mBouncerToGoneTransition; @Inject @@ -444,6 +471,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mBouncerToGoneTransition, mMainDispatcher); collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(), mScrimAlphaConsumer, mMainDispatcher); + + // LOCKSCREEN<->GLANCEABLE_HUB + collectFlow(behindScrim, + mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB), + mGlanceableHubConsumer, mMainDispatcher); + collectFlow(behindScrim, + mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN), + mGlanceableHubConsumer, mMainDispatcher); } // TODO(b/270984686) recompute scrim height accurately, based on shade contents. @@ -815,9 +850,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump return; } mBouncerHiddenFraction = bouncerHiddenAmount; - if (mState == ScrimState.DREAMING) { - // Only the dreaming state requires this for the scrim calculation, so we should - // only trigger an update if dreaming. + if (mState == ScrimState.DREAMING || mState == ScrimState.GLANCEABLE_HUB) { + // The dreaming and glanceable hub states requires this for the scrim calculation, so we + // should only trigger an update in those states. applyAndDispatchState(); } } @@ -939,7 +974,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) { mNotificationsAlpha = (float) Math.pow(getInterpolatedFraction(), 0.8f); } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED - || mState == ScrimState.PULSING) { + || mState == ScrimState.PULSING || mState == ScrimState.GLANCEABLE_HUB) { Pair<Integer, Float> result = calculateBackStateForState(mState); int behindTint = result.first; float behindAlpha = result.second; @@ -950,6 +985,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mTransitionToFullShadeProgress); behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first, mTransitionToFullShadeProgress); + } else if (mState == ScrimState.GLANCEABLE_HUB && mTransitionToFullShadeProgress == 0.0f + && mBouncerHiddenFraction == KeyguardBouncerConstants.EXPANSION_HIDDEN) { + // Behind scrim should not be visible when idle on the glanceable hub and neither + // bouncer nor shade are showing. + behindAlpha = 0f; } mInFrontAlpha = mState.getFrontAlpha(); if (mClipsQsScrim) { @@ -965,6 +1005,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (mState == ScrimState.SHADE_LOCKED) { // going from KEYGUARD to SHADE_LOCKED state mNotificationsAlpha = getInterpolatedFraction(); + } else if (mState == ScrimState.GLANCEABLE_HUB + && mTransitionToFullShadeProgress == 0.0f) { + // Notification scrim should not be visible on the glanceable hub unless the + // shade is showing or transitioning in. Otherwise the notification scrim will + // be visible as the bouncer transitions in or after the notification shade + // closes. + mNotificationsAlpha = 0; } else { mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 61bd112121bc..f2a649ba2e32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -296,6 +296,21 @@ public enum ScrimState { updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor); } } + }, + + /** + * Device is locked or on dream and user has swiped from the right edge to enter the glanceable + * hub UI. From this state, the user can swipe from the left edge to go back to the lock screen + * or dream, as well as swipe down for the notifications and up for the bouncer. + */ + GLANCEABLE_HUB { + @Override + public void prepare(ScrimState previousState) { + // No scrims should be visible by default in this state. + mBehindAlpha = 0; + mNotifAlpha = 0; + mFrontAlpha = 0; + } }; boolean mBlankScreen = false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 9c4984ee4769..849a13be58ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -41,6 +41,8 @@ import static org.mockito.Mockito.when; import static java.util.Collections.emptySet; +import static kotlinx.coroutines.flow.FlowKt.flowOf; + import android.app.ActivityManager; import android.app.IWallpaperManager; import android.app.WallpaperManager; @@ -77,7 +79,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.TestScopeProvider; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; @@ -92,6 +93,10 @@ import com.android.systemui.charging.WiredChargingRippleController; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.communal.data.repository.CommunalRepository; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; +import com.android.systemui.communal.shared.model.CommunalSceneKey; +import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; @@ -102,6 +107,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.log.LogBuffer; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.notetask.NoteTaskController; @@ -195,6 +201,8 @@ import java.util.Optional; import javax.inject.Provider; +import kotlinx.coroutines.test.TestScope; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -203,11 +211,17 @@ public class CentralSurfacesImplTest extends SysuiTestCase { private static final int FOLD_STATE_FOLDED = 0; private static final int FOLD_STATE_UNFOLDED = 1; + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + private CentralSurfacesImpl mCentralSurfaces; private FakeMetricsLogger mMetricsLogger; private PowerManager mPowerManager; private VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider; + + private final TestScope mTestScope = mKosmos.getTestScope(); + private final CommunalInteractor mCommunalInteractor = mKosmos.getCommunalInteractor(); + private final CommunalRepository mCommunalRepository = mKosmos.getCommunalRepository(); @Mock private NotificationsController mNotificationsController; @Mock private LightBarController mLightBarController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -461,7 +475,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { new DisplayMetrics(), mMetricsLogger, mShadeLogger, - new JavaAdapter(TestScopeProvider.getTestScope()), + new JavaAdapter(mTestScope), mUiBgExecutor, mNotificationPanelViewController, mNotificationMediaManager, @@ -473,6 +487,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mScreenLifecycle, mWakefulnessLifecycle, mPowerInteractor, + mCommunalInteractor, mStatusBarStateController, Optional.of(mBubbles), () -> mNoteTaskController, @@ -821,6 +836,25 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test + public void testEnteringGlanceableHub_updatesScrim() { + // Transition to the glanceable hub. + mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( + CommunalSceneKey.Communal.INSTANCE))); + mTestScope.getTestScheduler().runCurrent(); + + // ScrimState also transitions. + verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB); + + // Transition away from the glanceable hub. + mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( + CommunalSceneKey.Blank.INSTANCE))); + mTestScope.getTestScheduler().runCurrent(); + + // ScrimState goes back to UNLOCKED. + verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any()); + } + + @Test public void testShowKeyguardImplementation_setsState() { when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 423cc8478dda..3bde6e36a51f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -51,6 +51,7 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.testing.ViewUtils; import android.util.MathUtils; import android.view.View; @@ -59,13 +60,13 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.TestScopeProvider; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.KeyguardState; @@ -73,6 +74,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.scrim.ScrimView; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator; @@ -103,7 +105,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.test.TestScope; @RunWith(AndroidTestingRunner.class) @@ -112,13 +113,14 @@ import kotlinx.coroutines.test.TestScope; public class ScrimControllerTest extends SysuiTestCase { @Rule public Expect mExpect = Expect.create(); + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); private final FakeConfigurationController mConfigurationController = new FakeConfigurationController(); private final LargeScreenShadeInterpolator mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator(); - private final TestScope mTestScope = TestScopeProvider.getTestScope(); + private final TestScope mTestScope = mKosmos.getTestScope(); private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope()); private ScrimController mScrimController; @@ -145,10 +147,12 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @Mock private AlternateBouncerToGoneTransitionViewModel mAlternateBouncerToGoneTransitionViewModel; - @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final KeyguardTransitionInteractor mKeyguardTransitionInteractor = + mKosmos.getKeyguardTransitionInteractor(); + private final FakeKeyguardTransitionRepository mKeyguardTransitionRepository = + mKosmos.getKeyguardTransitionRepository(); @Mock private KeyguardInteractor mKeyguardInteractor; private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository(); - @Mock private CoroutineDispatcher mMainDispatcher; @Mock private TypedArray mMockTypedArray; // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The @@ -265,8 +269,6 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDelayedWakeLockFactory.create(any(String.class))).thenReturn(mWakeLock); when(mDockManager.isDocked()).thenReturn(false); - when(mKeyguardTransitionInteractor.transition(any(), any())) - .thenReturn(emptyFlow()); when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha()) .thenReturn(emptyFlow()); when(mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha()) @@ -292,13 +294,16 @@ public class ScrimControllerTest extends SysuiTestCase { mKeyguardTransitionInteractor, mKeyguardInteractor, mWallpaperRepository, - mMainDispatcher, + mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); + // Attach behind scrim so flows that are collecting on it start running. + ViewUtils.attachView(mScrimBehind); + mScrimController.setHasBackdrop(false); mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); @@ -629,6 +634,164 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void lockscreenToHubTransition_setsBehindScrimAlpha() { + // Start on lockscreen. + mScrimController.transitionTo(ScrimState.KEYGUARD); + finishAnimationsImmediately(); + + // Behind scrim starts at default alpha. + final float transitionProgress = 0f; + float expectedAlpha = ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + transitionProgress, + TransitionState.STARTED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim fades out as transition runs. + final float runningProgress = 0.2f; + expectedAlpha = (1 - runningProgress) * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + runningProgress, + TransitionState.RUNNING + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim invisible at end of transition. + final float finishedProgress = 1f; + expectedAlpha = 0f; + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + finishedProgress, + TransitionState.FINISHED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + } + + @Test + public void hubToLockscreenTransition_setsViewAlpha() { + // Start on glanceable hub. + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // Behind scrim starts at 0 alpha. + final float transitionProgress = 0f; + float expectedAlpha = 0f; + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + transitionProgress, + TransitionState.STARTED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim fades in as transition runs. + final float runningProgress = 0.2f; + expectedAlpha = runningProgress * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + runningProgress, + TransitionState.RUNNING + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim at default visibility at end of transition. + final float finishedProgress = 1f; + expectedAlpha = finishedProgress * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + finishedProgress, + TransitionState.FINISHED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + } + + @Test + public void transitionToHub() { + mScrimController.setRawPanelExpansionFraction(0f); + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN); + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims transparent on the hub. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test + public void openBouncerOnHub() { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + + // Open the bouncer. + mScrimController.setRawPanelExpansionFraction(0f); + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_VISIBLE); + finishAnimationsImmediately(); + + // Only behind widget is visible. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, OPAQUE)); + + // Bouncer is closed. + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN); + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims are transparent. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test + public void openShadeOnHub() { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + + // Open the shade. + mScrimController.transitionTo(SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); + finishAnimationsImmediately(); + + // Shade scrims are visible. + assertScrimAlpha(Map.of( + mNotificationsScrim, OPAQUE, + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE)); + + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims are transparent. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test public void onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor() { assertEquals(BOUNCER.getBehindTint(), 0x112233); mSurfaceColor = 0x223344; @@ -1001,7 +1164,7 @@ public class ScrimControllerTest extends SysuiTestCase { mKeyguardTransitionInteractor, mKeyguardInteractor, mWallpaperRepository, - mMainDispatcher, + mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); @@ -1267,7 +1430,7 @@ public class ScrimControllerTest extends SysuiTestCase { ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, BOUNCER, ScrimState.DREAMING, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED, SHADE_LOCKED, ScrimState.AUTH_SCRIMMED, - ScrimState.AUTH_SCRIMMED_SHADE)); + ScrimState.AUTH_SCRIMMED_SHADE, ScrimState.GLANCEABLE_HUB)); for (ScrimState state : ScrimState.values()) { if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) { 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 0c1dbfebfb34..e20a0ab4190e 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 @@ -28,9 +28,12 @@ import dagger.Module import java.util.UUID import javax.inject.Inject import junit.framework.Assert.fail +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -150,6 +153,15 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio _transitions.emit(step) } + /** Version of [sendTransitionStep] that's usable from Java tests. */ + fun sendTransitionStepJava( + coroutineScope: CoroutineScope, + step: TransitionStep, + validateStep: Boolean = true + ): Job { + return coroutineScope.launch { sendTransitionStep(step, validateStep) } + } + suspend fun sendTransitionSteps( steps: List<TransitionStep>, testScope: TestScope, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 11f2938141b4..083de107c971 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -32,6 +32,8 @@ import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteract import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.power.data.repository.fakePowerRepository @@ -61,6 +63,8 @@ class KosmosJavaAdapter( val bouncerRepository by lazy { kosmos.bouncerRepository } val communalRepository by lazy { kosmos.fakeCommunalRepository } val keyguardRepository by lazy { kosmos.fakeKeyguardRepository } + val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository } + val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor } val powerRepository by lazy { kosmos.fakePowerRepository } val clock by lazy { kosmos.systemClock } val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository } |