Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f2316cc authored by William Xiao's avatar William Xiao
Browse files

Add scrim transitions to/from glanceable hub

The glanceable hub is accessible from keyguard, but since it's
underneath all of the scrims, so that notifications/bouncer can show
over it, it's also dimmed by the lockscreen's back scrim which applies
a slight dimming to the lockscreen wallpaper.

This change adds a new ScrimState for the hub and implements a
transition to/from it as well as adjusting existing logic so that
opening the notification shade and bouncer on top of it work as
expected.

Bug: 315203484
Fixed: 315203484
Test: atest CentralSurfacesImplTest ScrimControllerTest
Flag: ACONFIG com.android.systemui.communal_hub DEVELOPMENT
Change-Id: I577e352eda47af4a03ffdff2aaa788f6f853238c
parent 976207c4
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -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) {
+51 −4
Original line number Diff line number Diff line
@@ -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);
                }
+15 −0
Original line number Diff line number Diff line
@@ -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;
+36 −2
Original line number Diff line number Diff line
@@ -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,
@@ -820,6 +835,25 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
    }

    @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<>());
+173 −10

File changed.

Preview size limit exceeded, changes collapsed.

Loading