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

Commit 417912a3 authored by Coco Duan's avatar Coco Duan
Browse files

Support dream starting in landscape on mobile

This change enables dream to start in the phone's current orientation
regardless of auto-rotation setting.
In NotificationShadeWindow, update layout orientation to SENSOR when
keyguard starts transitioning to dream or finishes on dream,
preventing an intermediate rotation to portrait during the transition.

Apply the same update to the glanceable hub, setting its layout
orientation to SENSOR as well.

Fixes: b/394842694
Fixes: b/402193097
Test: atest NotificationShadeWindowControllerImplTest
Test: atest NotificationShadeWindowModelTest
Flag: android.service.dreams.dreams_v2
Change-Id: I2d5253250ce91336d0c874d34d8c887bda6205e9
parent 412ec48c
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -579,6 +579,10 @@

    <!-- Auto-rotation behavior -->

    <!-- If this is true, then always allow auto-rotation when dreaming, regardless of the user's
         auto-rotation setting. -->
    <bool name="config_alwaysAllowDreamRotation">false</bool>

    <!-- If true, enables auto-rotation features using the accelerometer.
         Otherwise, auto-rotation is disabled.  Applications may still request
         to use specific orientations but the sensor is ignored and sensor-based
+1 −0
Original line number Diff line number Diff line
@@ -2076,6 +2076,7 @@
  <java-symbol type="bool" name="config_allowTheaterModeWakeFromDock" />
  <java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
  <java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
  <java-symbol type="bool" name="config_alwaysAllowDreamRotation" />
  <java-symbol type="bool" name="config_glanceableHubEnabled" />
  <java-symbol type="integer" name="config_whenToStartHubModeDefault" />
  <java-symbol type="integer" name="config_keyguardDrawnTimeout" />
+73 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.shade;

import static android.service.dreams.Flags.FLAG_DREAMS_V2;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
@@ -40,6 +41,8 @@ import android.app.IActivityManager;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -145,6 +148,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
                R.integer.config_keyguardRefreshRate,
                (int) mPreferredRefreshRate
        );
        overrideResource(com.android.internal.R.bool.config_alwaysAllowDreamRotation, true);

        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
        when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
@@ -398,16 +402,83 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
    }

    @Test
    public void hubOrientationAware_layoutParamsUpdated() {
    public void hubOrientationAware_orientationSensor() {
        mNotificationShadeWindowController.setKeyguardShowing(false);
        mNotificationShadeWindowController.setBouncerShowing(false);
        mNotificationShadeWindowController.onIsOnOrGoingToDreamChanged(false);
        mNotificationShadeWindowController.setGlanceableHubOrientationAware(true);
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
        mNotificationShadeWindowController.onConfigChanged(new Configuration());

        verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
        assertThat(mLayoutParameters.getValue().screenOrientation)
                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_USER);
                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    }

    @Test
    @EnableFlags(FLAG_DREAMS_V2)
    public void isGoingToDream_orientationSensor_dreamsV2FlagEnabled() {
        mNotificationShadeWindowController.setKeyguardShowing(true);
        // transitioning to dream
        mNotificationShadeWindowController.onIsOnOrGoingToDreamChanged(true);
        // keyguard not yet occluded by dream
        mNotificationShadeWindowController.setKeyguardOccluded(false);
        mNotificationShadeWindowController.setGlanceableHubOrientationAware(false);
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
        mNotificationShadeWindowController.onConfigChanged(new Configuration());

        verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
        assertThat(mLayoutParameters.getValue().screenOrientation)
                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    }

    @Test
    @EnableFlags(FLAG_DREAMS_V2)
    public void isKeyguardOccludedByDream_orientationSensor_dreamsV2FlagEnabled() {
        mNotificationShadeWindowController.setKeyguardShowing(true);
        mNotificationShadeWindowController.setGlanceableHubOrientationAware(false);
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
        mNotificationShadeWindowController.onIsOnOrGoingToDreamChanged(true);
        // occluded by dream
        mNotificationShadeWindowController.setKeyguardOccluded(true);
        mNotificationShadeWindowController.onConfigChanged(new Configuration());

        verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
        assertThat(mLayoutParameters.getValue().screenOrientation)
                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    }

    @Test
    @DisableFlags(FLAG_DREAMS_V2)
    public void isGoingToDream_orientationNoSensor_dreamsV2FlagDisabled() {
        mNotificationShadeWindowController.setKeyguardShowing(true);
        mNotificationShadeWindowController.setGlanceableHubOrientationAware(false);
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
        // transitioning to dream
        mNotificationShadeWindowController.onIsOnOrGoingToDreamChanged(true);
        // keyguard not yet occluded by dream
        mNotificationShadeWindowController.setKeyguardOccluded(false);
        mNotificationShadeWindowController.onConfigChanged(new Configuration());

        verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
        assertThat(mLayoutParameters.getValue().screenOrientation)
                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
    }

    @Test
    @DisableFlags(FLAG_DREAMS_V2)
    public void isKeyguardOccludedByDream_orientationUnspecified_dreamsV2FlagDisabled() {
        mNotificationShadeWindowController.setKeyguardShowing(true);
        mNotificationShadeWindowController.setGlanceableHubOrientationAware(false);
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
        mNotificationShadeWindowController.onIsOnOrGoingToDreamChanged(true);
        // occluded by dream
        mNotificationShadeWindowController.setKeyguardOccluded(true);
        mNotificationShadeWindowController.onConfigChanged(new Configuration());

        verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture());
        assertThat(mLayoutParameters.getValue().screenOrientation)
                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }

    @Test
+83 −15
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepos
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.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Overlays
@@ -161,23 +163,89 @@ class NotificationShadeWindowModelTest : SysuiTestCase() {
        }

    @Test
    @EnableSceneContainer
    fun withSceneContainer_bouncerShowing_providesTheCorrectState() =
        testScope.runTest {
            val bouncerShowing by collectLastValue(underTest.isBouncerShowing)
    fun isOnOrGoingToDream_whenTransitioningToDreaming_isTrue() =
        kosmos.runTest {
            val isOnOrGoingToDream by collectLastValue(underTest.isOnOrGoingToDream)
            assertThat(isOnOrGoingToDream).isFalse()

            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Idle(Scenes.Lockscreen)
            fakeKeyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    TransitionStep(
                        from = KeyguardState.OCCLUDED,
                        to = KeyguardState.DREAMING,
                        value = 0f,
                        transitionState = TransitionState.STARTED,
                    ),
                    TransitionStep(
                        from = KeyguardState.OCCLUDED,
                        to = KeyguardState.DREAMING,
                        value = 0.5f,
                        transitionState = TransitionState.RUNNING,
                    ),
                ),
                testScope,
            )
            kosmos.sceneInteractor.setTransitionState(transitionState)
            runCurrent()
            assertThat(bouncerShowing).isFalse()
            assertThat(isOnOrGoingToDream).isTrue()
        }

            transitionState.value =
                ObservableTransitionState.Idle(Scenes.Lockscreen, setOf(Overlays.Bouncer))
            runCurrent()
            assertThat(bouncerShowing).isTrue()
    @Test
    fun isOnOrGoingToDream_whenTransitionToDreamingFinished_isTrue() =
        kosmos.runTest {
            val isOnOrGoingToDream by collectLastValue(underTest.isOnOrGoingToDream)
            assertThat(isOnOrGoingToDream).isFalse()

            fakeKeyguardTransitionRepository.transitionTo(
                from = KeyguardState.OCCLUDED,
                to = KeyguardState.DREAMING,
            )
            assertThat(isOnOrGoingToDream).isTrue()
        }

    @Test
    fun isOnOrGoingToDream_whenTransitioningAwayFromDreaming_isFalse() =
        kosmos.runTest {
            val isOnOrGoingToDream by collectLastValue(underTest.isOnOrGoingToDream)
            keyguardTransitionRepository.transitionTo(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.DREAMING,
            )
            assertThat(isOnOrGoingToDream).isTrue()

            fakeKeyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    TransitionStep(
                        from = KeyguardState.DREAMING,
                        to = KeyguardState.LOCKSCREEN,
                        value = 0f,
                        transitionState = TransitionState.STARTED,
                    ),
                    TransitionStep(
                        from = KeyguardState.DREAMING,
                        to = KeyguardState.LOCKSCREEN,
                        value = 0.5f,
                        transitionState = TransitionState.RUNNING,
                    ),
                ),
                testScope,
            )
            assertThat(isOnOrGoingToDream).isFalse()
        }

    @Test
    fun isOnOrGoingToDream_whenFinishedTransitionAwayFromDreaming_isFalse() =
        kosmos.runTest {
            val isOnOrGoingToDream by collectLastValue(underTest.isOnOrGoingToDream)
            keyguardTransitionRepository.transitionTo(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.DREAMING,
            )
            assertThat(isOnOrGoingToDream).isTrue()

            keyguardTransitionRepository.transitionTo(
                from = KeyguardState.DREAMING,
                to = KeyguardState.LOCKSCREEN,
            )
            assertThat(isOnOrGoingToDream).isFalse()
        }

    @Test
+24 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.shade;

import static android.service.dreams.Flags.dreamsV2;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;

@@ -354,6 +355,14 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
                mCommunalInteractor.get().isCommunalVisible(),
                this::onCommunalVisibleChanged
        );
        if (dreamsV2() && mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_alwaysAllowDreamRotation)) {
            collectFlow(
                    mWindowRootView,
                    mNotificationShadeWindowModel.isOnOrGoingToDream(),
                    this::onIsOnOrGoingToDreamChanged
            );
        }

        if (!SceneContainerFlag.isEnabled() && Flags.useTransitionsForKeyguardOccluded()) {
            collectFlow(
@@ -463,14 +472,18 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
    }

    private void adjustScreenOrientation(NotificationShadeWindowState state) {
        if (state.bouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.dozing) {
        boolean dreamShowingAndRotationAllowed = dreamsV2() ? mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_alwaysAllowDreamRotation)
                && state.isOnOrGoingToDream : false;
        if (state.bouncerShowing || (state.isKeyguardShowingAndNotOccluded()
                && !dreamShowingAndRotationAllowed) || state.dozing) {
            if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
                mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
            } else {
                mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
            }
        } else if (state.glanceableHubOrientationAware) {
            mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
        } else if (state.glanceableHubOrientationAware || dreamShowingAndRotationAllowed) {
            mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
        } else {
            mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
        }
@@ -670,7 +683,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
                state.dozing,
                state.scrimsVisibility,
                state.backgroundBlurRadius,
                state.communalVisible
                state.communalVisible,
                state.isOnOrGoingToDream
        );
    }

@@ -813,6 +827,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
        apply(mCurrentState);
    }

    @VisibleForTesting
    void onIsOnOrGoingToDreamChanged(Boolean isOnOrGoingToDream) {
        mCurrentState.isOnOrGoingToDream = isOnOrGoingToDream;
        apply(mCurrentState);
    }

    @Override
    public void setForceUserActivity(boolean forceUserActivity) {
        mCurrentState.forceUserActivity = forceUserActivity;
Loading