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

Commit 20690484 authored by Coco Duan's avatar Coco Duan Committed by Android (Google) Code Review
Browse files

Merge changes from topic "hub-rotation-bouncer-fix" into main

* changes:
  Prevent landscape bouncer from showing when opened from hub
  Allow device rotation on glanceable hub
parents 2cd23de9 69f5eb75
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.OnPreDrawListener
import android.view.WindowInsetsController
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -719,6 +721,37 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
        verify(inputViewController).onStartingToHide()
    }

    @Test
    fun startAppearAnimation_ifDelayed() {
        val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
        whenever(view.isAppearAnimationDelayed).thenReturn(true)
        val viewTreeObserver: ViewTreeObserver = mock()
        whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)

        underTest.startAppearAnimationIfDelayed()

        verify(view).alpha = 1f
        verify(viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
        argumentCaptor.value.onPreDraw()

        verify(view).startAppearAnimation(any(SecurityMode::class.java))
        verify(view).setIsAppearAnimationDelayed(false)
    }

    @Test
    fun appearAnimation_willNotStart_ifNotDelayed() {
        whenever(view.isAppearAnimationDelayed).thenReturn(false)
        val viewTreeObserver: ViewTreeObserver = mock()
        whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)

        underTest.startAppearAnimationIfDelayed()

        verify(view, never()).alpha
        verify(viewTreeObserver, never()).addOnPreDrawListener(any())

        verify(view, never()).startAppearAnimation(any(SecurityMode::class.java))
    }

    @Test
    fun gravityReappliedOnConfigurationChange() {
        // Set initial gravity
+8 −0
Original line number Diff line number Diff line
@@ -452,6 +452,14 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
        verify(keyguardPasswordView).setDisappearAnimationListener(any());
    }

    @Test
    public void setupForDelayedAppear() {
        mKeyguardSecurityContainer.setupForDelayedAppear();
        assertThat(mKeyguardSecurityContainer.getTranslationY()).isEqualTo(0f);
        assertThat(mKeyguardSecurityContainer.getAlpha()).isEqualTo(0f);
        assertThat(mKeyguardSecurityContainer.isAppearAnimationDelayed()).isTrue();
    }

    private BackEvent createBackEvent(float touchX, float progress) {
        return new BackEvent(0, 0, progress, BackEvent.EDGE_LEFT);
    }
+17 −1
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import static org.mockito.Mockito.when;

import android.animation.ValueAnimator;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -54,6 +56,7 @@ import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;

import org.junit.Before;
@@ -127,10 +130,16 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
    @Mock
    WindowRootView mWindowRootView;

    @Mock
    Resources mResources;

    private SceneInteractor mSceneInteractor;

    private KeyguardStateController mKeyguardStateController;

    private static final float TOUCH_REGION = .3f;
    private static final float MIN_BOUNCER_HEIGHT = .05f;
    private final Configuration mConfiguration = new Configuration();

    private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100);
    private static final UserInfo CURRENT_USER_INFO = new UserInfo(
@@ -153,6 +162,8 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
    public void setup() {
        mKosmos = new KosmosJavaAdapter(this);
        mSceneInteractor = spy(mKosmos.getSceneInteractor());
        mKeyguardStateController = mKosmos.getKeyguardStateController();
        mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;

        MockitoAnnotations.initMocks(this);
        mTouchHandler = new BouncerSwipeTouchHandler(
@@ -172,7 +183,9 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
                mKeyguardInteractor,
                mSceneInteractor,
                mKosmos.getShadeRepository(),
                Optional.of(() -> mWindowRootView));
                Optional.of(() -> mWindowRootView),
                mKeyguardStateController,
                mKosmos.getCommunalSettingsInteractor());

        when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
        when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -180,6 +193,9 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
        when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
        when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
        when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
        when(mWindowRootView.getResources()).thenReturn(mResources);
        when(mResources.getConfiguration()).thenReturn(mConfiguration);
    }

    /**
+74 −2
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.systemui.ambient.touch;

import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;

import static com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2;

import static com.google.common.truth.Truth.assertThat;

import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
@@ -34,6 +38,8 @@ import static org.mockito.Mockito.when;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.Region;
import android.platform.test.annotations.DisableFlags;
@@ -63,6 +69,7 @@ import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;

import org.junit.Before;
@@ -132,12 +139,16 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
    @Mock
    WindowRootView mWindowRootView;

    Resources mResources;

    @Mock
    CommunalViewModel mCommunalViewModel;

    @Mock
    KeyguardInteractor mKeyguardInteractor;

    private KeyguardStateController mKeyguardStateController;

    @Captor
    ArgumentCaptor<Rect> mRectCaptor;

@@ -147,6 +158,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
    private static final int SCREEN_WIDTH_PX = 1024;
    private static final int SCREEN_HEIGHT_PX = 100;
    private static final float MIN_BOUNCER_HEIGHT = .05f;
    private final Configuration mConfiguration = new Configuration();

    private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100);
    private static final UserInfo CURRENT_USER_INFO = new UserInfo(
@@ -157,7 +169,8 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {

    @Parameters(name = "{0}")
    public static List<FlagsParameterization> getParams() {
        return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
        return SceneContainerFlagParameterizationKt
                .andSceneContainer(allCombinationsOf(Flags.FLAG_GLANCEABLE_HUB_V2));
    }

    public BouncerSwipeTouchHandlerTest(FlagsParameterization flags) {
@@ -168,7 +181,13 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
    @Before
    public void setup() {
        mKosmos = new KosmosJavaAdapter(this);
        mContext.ensureTestableResources();
        mResources = mContext.getResources();
        overrideConfiguration(mConfiguration);
        mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;

        mSceneInteractor = spy(mKosmos.getSceneInteractor());
        mKeyguardStateController = mKosmos.getKeyguardStateController();

        MockitoAnnotations.initMocks(this);
        mTouchHandler = new BouncerSwipeTouchHandler(
@@ -188,7 +207,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
                mKeyguardInteractor,
                mSceneInteractor,
                mKosmos.getShadeRepository(),
                Optional.of(() -> mWindowRootView)
                Optional.of(() -> mWindowRootView),
                mKeyguardStateController,
                mKosmos.getCommunalSettingsInteractor()
        );

        when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
@@ -197,6 +218,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
        when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
        when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
        when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
        when(mWindowRootView.getResources()).thenReturn(mResources);
        setCommunalV2ConfigEnabled(true);
    }

    /**
@@ -586,6 +610,43 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
        verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE);
    }

    @Test
    @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
    public void swipeUpAboveThresholdInLandscape_keyguardRotationNotAllowed_showsBouncer() {
        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
        mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;

        final float swipeUpPercentage = .1f;
        // The upward velocity is ignored.
        final float velocityY = -1;
        swipeToPosition(swipeUpPercentage, velocityY);

        // Ensure show bouncer scrimmed
        verify(mScrimController).show(true);
        verify(mValueAnimatorCreator, never()).create(anyFloat(), anyFloat());
        verify(mValueAnimator, never()).start();
    }

    @Test
    @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
    public void swipeUpBelowThreshold_inLandscapeKeyguardRotationNotAllowed_noBouncer() {
        mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;

        final float swipeUpPercentage = .02f;
        // The upward velocity is ignored.
        final float velocityY = -1;
        swipeToPosition(swipeUpPercentage, velocityY);

        // no bouncer shown scrimmed
        verify(mScrimController, never()).show(true);
        // on touch end, bouncer hidden
        verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
                eq(KeyguardBouncerConstants.EXPANSION_HIDDEN));
        verify(mValueAnimator, never()).addListener(any());
    }

    /**
     * Tests that swiping up with a speed above the set threshold will continue the expansion.
     */
@@ -672,4 +733,15 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {

        inputEventListenerCaptor.getValue().onInputEvent(upEvent);
    }

    private void setCommunalV2ConfigEnabled(boolean enabled) {
        mContext.getOrCreateTestableResources().addOverride(
                com.android.internal.R.bool.config_glanceableHubEnabled,
                enabled);
    }

    private void overrideConfiguration(Configuration configuration) {
        mContext.getOrCreateTestableResources().overrideConfiguration(
                configuration);
    }
}
+114 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
@@ -38,8 +39,13 @@ import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
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.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
@@ -56,11 +62,15 @@ import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.kotlin.verify
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters

@@ -93,10 +103,12 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
                communalInteractor = communalInteractor,
                communalSettingsInteractor = communalSettingsInteractor,
                communalSceneInteractor = communalSceneInteractor,
                keyguardTransitionInteractor = keyguardTransitionInteractor,
                keyguardInteractor = keyguardInteractor,
                systemSettings = fakeSettings,
                notificationShadeWindowController = notificationShadeWindowController,
                bgScope = applicationCoroutineScope,
                applicationScope = applicationCoroutineScope,
                mainDispatcher = testDispatcher,
                uiEventLogger = uiEventLoggerFake,
            )
@@ -111,13 +123,13 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
                UserHandle.USER_CURRENT,
            )
            fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
            setCommunalV2ConfigEnabled(true)

            underTest.start()

            // Make communal available so that communalInteractor.desiredScene accurately reflects
            // scene changes instead of just returning Blank.
            runBlocking { setCommunalAvailable(true) }
            setCommunalV2ConfigEnabled(true)
        }
    }

@@ -414,6 +426,107 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase()
            assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
        }

    @Test
    @DisableFlags(FLAG_SCENE_CONTAINER)
    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
    fun glanceableHubOrientationAware_idleOnCommunal() =
        kosmos.runTest {
            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")

            val scene by collectLastValue(communalSceneInteractor.currentScene)
            assertThat(scene).isEqualTo(CommunalScenes.Communal)

            verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
        }

    @Test
    @DisableFlags(FLAG_SCENE_CONTAINER)
    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
    fun glanceableHubOrientationAware_transitioningToCommunal() =
        kosmos.runTest {
            val progress = MutableStateFlow(0f)
            val targetScene = CommunalScenes.Communal
            val currentScene = CommunalScenes.Blank
            val transitionState =
                MutableStateFlow(
                    ObservableTransitionState.Transition(
                        fromScene = currentScene,
                        toScene = targetScene,
                        currentScene = flowOf(targetScene),
                        progress = progress,
                        isInitiatedByUserInput = false,
                        isUserInputOngoing = flowOf(false),
                    )
                )
            communalSceneInteractor.setTransitionState(transitionState)

            // Partially transition.
            progress.value = .4f

            val scene by collectLastValue(communalSceneInteractor.currentScene)
            assertThat(scene).isEqualTo(CommunalScenes.Blank)

            verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
        }

    @Test
    @DisableFlags(FLAG_SCENE_CONTAINER)
    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
    fun glanceableHubOrientationAware_communalToDreaming() =
        kosmos.runTest {
            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")

            verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
            Mockito.clearInvocations(notificationShadeWindowController)

            val progress = MutableStateFlow(0f)
            val currentScene = CommunalScenes.Communal
            val targetScene = CommunalScenes.Blank
            val transitionState =
                MutableStateFlow(
                    ObservableTransitionState.Transition(
                        fromScene = currentScene,
                        toScene = targetScene,
                        currentScene = flowOf(targetScene),
                        progress = progress,
                        isInitiatedByUserInput = false,
                        isUserInputOngoing = flowOf(false),
                    )
                )
            communalSceneInteractor.setTransitionState(transitionState)

            // Partially transitioned out of Communal scene
            progress.value = .4f

            // Started keyguard transitioning from hub -> dreaming.
            fakeKeyguardTransitionRepository.sendTransitionStep(
                TransitionStep(
                    from = KeyguardState.GLANCEABLE_HUB,
                    to = KeyguardState.DREAMING,
                    transitionState = TransitionState.STARTED,
                )
            )
            verify(notificationShadeWindowController).setGlanceableHubOrientationAware(true)
            Mockito.clearInvocations(notificationShadeWindowController)

            fakeKeyguardTransitionRepository.sendTransitionStep(
                from = KeyguardState.GLANCEABLE_HUB,
                to = KeyguardState.DREAMING,
                transitionState = TransitionState.RUNNING,
                value = 0.5f,
            )

            // Transitioned to dreaming.
            fakeKeyguardTransitionRepository.sendTransitionStep(
                from = KeyguardState.GLANCEABLE_HUB,
                to = KeyguardState.DREAMING,
                transitionState = TransitionState.FINISHED,
                value = 1f,
            )
            // Not on hub anymore, let other states take control
            verify(notificationShadeWindowController).setGlanceableHubOrientationAware(false)
        }

    /**
     * Advances time by duration + 1 millisecond, to ensure that tasks scheduled to run at
     * currentTime + duration are scheduled.
Loading