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

Commit 13f8b3fe authored by Daniel Akinola's avatar Daniel Akinola Committed by Android (Google) Code Review
Browse files

Merge "Ensure that shade moves to touched display before expanding" into main

parents 2747593a 6d6d1d3a
Loading
Loading
Loading
Loading
+12 −12
Original line number Diff line number Diff line
@@ -64,35 +64,35 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
    }

    @Test
    fun onStatusBarTouched_called_updatesDisplayId() =
    fun onStatusBarOrLauncherTouched_called_updatesDisplayId() =
        testScope.runTest {
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
            underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
            underTest.onStatusBarOrLauncherTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)

            assertThat(displayId).isEqualTo(2)
        }

    @Test
    fun onStatusBarTouched_notExistentDisplay_displayIdNotUpdated() =
    fun onStatusBarOrLauncherTouched_notExistentDisplay_displayIdNotUpdated() =
        testScope.runTest {
            val displayIds by collectValues(underTest.displayId)
            assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))

            underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
            underTest.onStatusBarOrLauncherTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)

            // Never set, as 2 was not a display according to the repository.
            assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
        }

    @Test
    fun onStatusBarTouched_afterDisplayRemoved_goesBackToDefaultDisplay() =
    fun onStatusBarOrLauncherTouched_afterDisplayRemoved_goesBackToDefaultDisplay() =
        testScope.runTest {
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
            underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
            underTest.onStatusBarOrLauncherTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)

            assertThat(displayId).isEqualTo(2)

@@ -102,9 +102,9 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
        }

    @Test
    fun onStatusBarTouched_leftSide_intentSetToNotifications() =
    fun onStatusBarOrLauncherTouched_leftSide_intentSetToNotifications() =
        testScope.runTest {
            underTest.onStatusBarTouched(
            underTest.onStatusBarOrLauncherTouched(
                createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
                STATUS_BAR_WIDTH,
            )
@@ -113,9 +113,9 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
        }

    @Test
    fun onStatusBarTouched_rightSide_intentSetToQs() =
    fun onStatusBarOrLauncherTouched_rightSide_intentSetToQs() =
        testScope.runTest {
            underTest.onStatusBarTouched(
            underTest.onStatusBarOrLauncherTouched(
                createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.95f),
                STATUS_BAR_WIDTH,
            )
@@ -124,9 +124,9 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
        }

    @Test
    fun onStatusBarTouched_nullAfterConsumed() =
    fun onStatusBarOrLauncherTouched_nullAfterConsumed() =
        testScope.runTest {
            underTest.onStatusBarTouched(
            underTest.onStatusBarOrLauncherTouched(
                createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
                STATUS_BAR_WIDTH,
            )
+39 −2
Original line number Diff line number Diff line
@@ -63,10 +63,12 @@ import android.os.Looper;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -110,6 +112,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.shared.recents.ILauncherProxy;
@@ -126,6 +129,8 @@ import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInterface;

import dagger.Lazy;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -137,8 +142,6 @@ import java.util.function.Supplier;
import javax.inject.Inject;
import javax.inject.Provider;

import dagger.Lazy;

/**
 * Class to send information from SysUI to Launcher with a binder.
 */
@@ -168,6 +171,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
    private final NotificationShadeWindowController mStatusBarWinController;
    private final Provider<SceneInteractor> mSceneInteractor;
    private final Provider<ShadeInteractor> mShadeInteractor;
    private final StatusBarTouchShadeDisplayPolicy mShadeDisplayPolicy;

    private final Runnable mConnectionRunnable = () ->
            internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
@@ -224,6 +228,12 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
        // TODO: change the method signature to use (boolean inputFocusTransferStarted)
        @Override
        public void onStatusBarTouchEvent(MotionEvent event) {
            moveShadeWindowIfNeeded(event);
            if (shouldIgnoreEvent(event)) {
                Log.d(TAG_OPS, "Ignoring launcher swipe event for legacy shade due to touch event"
                        + " on display without notification shade");
                return;
            }
            verifyCallerAndClearCallingIdentity("onStatusBarTouchEvent", () -> {
                if (SceneContainerFlag.isEnabled()) {
                    //TODO(b/329863123) implement latency tracking for shade scene
@@ -269,6 +279,31 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
            });
        }

        @VisibleForTesting
        public void moveShadeWindowIfNeeded(MotionEvent event) {
            if (ShadeWindowGoesAround.isEnabled() && SceneContainerFlag.isEnabled()) {
                Trace.beginSection("LauncherProxyService#moveShadeWindowIfNeeded");
                // TODO: b/407496148 - Refactor to use DisplayMetricsRepository instead
                final DisplayInfo displayInfo = new DisplayInfo();
                mDisplayTracker.getDisplay(event.getDisplayId()).getDisplayInfo(displayInfo);
                int displayWidth = displayInfo.logicalWidth;
                mShadeDisplayPolicy.onStatusBarOrLauncherTouched(event, displayWidth);
                Trace.endSection();
            }
        }

        @VisibleForTesting
        public boolean shouldIgnoreEvent(MotionEvent event) {
            if (ShadeWindowGoesAround.isEnabled() && !SceneContainerFlag.isEnabled()) {
                // For legacy shade case, don't attempt to handle touch events on display that
                // doesn't have the shade. They're handled with SceneContainerFlag enabled.
                boolean touchingDisplayWithoutShade =
                        event.getDisplayId() != mShadeDisplayPolicy.getDisplayId().getValue();
                return touchingDisplayWithoutShade;
            }
            return false;
        }

        @Override
        public void onStatusBarTrackpadEvent(MotionEvent event) {
            verifyCallerAndClearCallingIdentityPostMain("onStatusBarTrackpadEvent", () -> {
@@ -694,6 +729,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
            PerDisplayRepository<SysUiState> perDisplaySysUiStateRepository,
            Provider<SceneInteractor> sceneInteractor,
            Provider<ShadeInteractor> shadeInteractor,
            StatusBarTouchShadeDisplayPolicy shadeDisplayPolicy,
            UserTracker userTracker,
            UserManager userManager,
            WakefulnessLifecycle wakefulnessLifecycle,
@@ -733,6 +769,7 @@ public class LauncherProxyService implements CallbackController<LauncherProxyLis
        mStatusBarWinController = statusBarWinController;
        mSceneInteractor = sceneInteractor;
        mShadeInteractor = shadeInteractor;
        mShadeDisplayPolicy = shadeDisplayPolicy;
        mUserTracker = userTracker;
        mConnectionBackoffAttempts = 0;
        mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
+2 −2
Original line number Diff line number Diff line
@@ -67,8 +67,8 @@ constructor(

    private var removalListener: Job? = null

    /** Called when the status bar on the given display is touched. */
    fun onStatusBarTouched(event: MotionEvent, statusBarWidth: Int) {
    /** Called when the status bar or launcher homescreen on the given display is touched. */
    fun onStatusBarOrLauncherTouched(event: MotionEvent, statusBarWidth: Int) {
        ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
        updateShadeDisplayIfNeeded(event)
        updateExpansionIntent(event, statusBarWidth)
+1 −1
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ private constructor(
            // Notify the shade display policy that the status bar was touched. This may cause
            // the shade to change display if the touch was in a display different than the shade
            // one.
            lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width)
            lazyStatusBarShadeDisplayPolicy.get().onStatusBarOrLauncherTouched(event, mView.width)
        }
    }

+100 −12
Original line number Diff line number Diff line
@@ -22,10 +22,11 @@ import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.PowerManager
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableContext
import android.testing.TestableLooper
import android.view.Display
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.app.AssistUtils
@@ -49,9 +50,11 @@ import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.views.NavigationBar
import com.android.systemui.process.ProcessWrapper
import com.android.systemui.recents.LauncherProxyService.ACTION_QUICKSTEP
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shared.recents.ILauncherProxy
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK
import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_ASLEEP
@@ -69,6 +72,7 @@ import com.android.wm.shell.sysui.ShellInterface
import com.google.common.util.concurrent.MoreExecutors
import java.util.Optional
import java.util.concurrent.Executor
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -77,6 +81,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
@@ -90,6 +95,9 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.argThat
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.never
import org.mockito.kotlin.whenever

@SmallTest
@@ -119,6 +127,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    @Mock private lateinit var shellInterface: ShellInterface
    @Mock private lateinit var navBarController: NavigationBarController
    @Mock private lateinit var shadeViewController: ShadeViewController
    @Mock private lateinit var sceneInteractor: SceneInteractor
    @Mock private lateinit var screenPinningRequest: ScreenPinningRequest
    @Mock private lateinit var navModeController: NavigationModeController
    @Mock private lateinit var statusBarWinController: NotificationShadeWindowController
@@ -134,6 +143,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    private lateinit var unfoldTransitionProgressForwarder:
        Optional<UnfoldTransitionProgressForwarder>
    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
    @Mock private lateinit var statusBarShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy
    @Mock private lateinit var backAnimation: Optional<BackAnimation>

    @Before
@@ -142,14 +152,14 @@ class LauncherProxyServiceTest : SysuiTestCase() {

        val serviceComponent = ComponentName("test_package", "service_provider")
        context.addMockService(serviceComponent, launcherProxy)
        context.addMockServiceResolver(
            TestableContext.MockServiceResolver {
        context.addMockServiceResolver {
            if (it.action == ACTION_QUICKSTEP) serviceComponent else null
        }
        )
        whenever(launcherProxy.queryLocalInterface(ArgumentMatchers.anyString()))
            .thenReturn(launcherProxy)
        whenever(launcherProxy.asBinder()).thenReturn(launcherProxy)
        doNothing().whenever(sceneInteractor).onRemoteUserInputStarted(anyString())
        doNothing().whenever(shadeViewController).startInputFocusTransfer()

        // packageManager.resolveServiceAsUser has to return non-null for
        // LauncherProxyService#isEnabled to become true.
@@ -157,8 +167,6 @@ class LauncherProxyServiceTest : SysuiTestCase() {
        whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
            .thenReturn(mock(ResolveInfo::class.java))

        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)

        // return isSystemUser as true by default.
        `when`(processWrapper.isSystemUser).thenReturn(true)
        sysuiStatePerDisplayRepository.add(Display.DEFAULT_DISPLAY, sysUiState)
@@ -172,6 +180,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun wakefulnessLifecycle_dispatchFinishedWakingUpSetsSysUIflagToAWAKE() {
        // WakefulnessLifecycle is initialized to AWAKE initially, and won't emit a noop.
        wakefulnessLifecycle.dispatchFinishedGoingToSleep()
@@ -187,6 +196,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun wakefulnessLifecycle_dispatchStartedWakingUpSetsSysUIflagToWAKING() {
        wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN)

@@ -198,6 +208,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun wakefulnessLifecycle_dispatchFinishedGoingToSleepSetsSysUIflagToASLEEP() {
        wakefulnessLifecycle.dispatchFinishedGoingToSleep()

@@ -209,6 +220,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun wakefulnessLifecycle_dispatchStartedGoingToSleepSetsSysUIflagToGOING_TO_SLEEP() {
        wakefulnessLifecycle.dispatchStartedGoingToSleep(
            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
@@ -222,6 +234,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun connectToLauncherService_primaryUserNoVisibleBgUsersSupported_expectBindService() {
        `when`(processWrapper.isSystemUser).thenReturn(true)
        `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
@@ -232,6 +245,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun connectToLauncherService_nonPrimaryUserNoVisibleBgUsersSupported_expectNoBindService() {
        `when`(processWrapper.isSystemUser).thenReturn(false)
        `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
@@ -242,6 +256,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun connectToLauncherService_nonPrimaryBgUserVisibleBgUsersSupported_expectBindService() {
        `when`(processWrapper.isSystemUser).thenReturn(false)
        `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
@@ -253,6 +268,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun connectToLauncherService_nonPrimaryFgUserVisibleBgUsersSupported_expectNoBindService() {
        `when`(processWrapper.isSystemUser).thenReturn(false)
        `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
@@ -264,6 +280,7 @@ class LauncherProxyServiceTest : SysuiTestCase() {
    }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun notifySysUiStateFlagsForAllDisplays_triggersUpdateInAllDisplays() =
        kosmos.testScope.runTest {
            kosmos.displayRepository.apply {
@@ -285,15 +302,14 @@ class LauncherProxyServiceTest : SysuiTestCase() {

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun updateSystemUiStateFlags_updatesAllNavBars() =
        kosmos.testScope.runTest {
            kosmos.displayRepository.apply {
                addDisplay(0)
                addDisplay(1)
            }
            kosmos.fakeSysUIStatePerDisplayRepository.apply {
                add(1, sysUiStateFactory.create(1))
            }
            kosmos.fakeSysUIStatePerDisplayRepository.apply { add(1, sysUiStateFactory.create(1)) }
            val navBar0 = mock<NavigationBar>()
            val navBar1 = mock<NavigationBar>()
            whenever(navBarController.getNavigationBar(eq(0))).thenReturn(navBar0)
@@ -305,6 +321,77 @@ class LauncherProxyServiceTest : SysuiTestCase() {
            verify(navBar1).updateSystemUiStateFlags()
        }

    @Test
    @EnableFlags(
        Flags.FLAG_SHADE_WINDOW_GOES_AROUND,
        Flags.FLAG_SCENE_CONTAINER,
        Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
    )
    fun onStatusBarTouchEvent_withSceneFlag_callsOnLauncherDrag() =
        kosmos.testScope.runTest {
            val shadeDisplayId = 1
            whenever(statusBarShadeDisplayPolicy.displayId)
                .thenReturn(MutableStateFlow(shadeDisplayId))

            val event =
                MotionEvent.obtain(500, 500, MotionEvent.ACTION_MOVE, 500f, 500f, 0).apply {
                    displayId = 0
                }

            subject.mSysUiProxy.onStatusBarTouchEvent(event)
            verify(statusBarShadeDisplayPolicy)
                .onStatusBarOrLauncherTouched(
                    argThat<MotionEvent> { displayId == event.displayId },
                    anyInt(),
                )
        }

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    @DisableFlags(Flags.FLAG_SCENE_CONTAINER, Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun onStatusBarTouchEvent_withoutSceneFlag_onDifferentDisplayTouch_ignoresInput() =
        kosmos.testScope.runTest {
            val shadeDisplayId = 1
            whenever(statusBarShadeDisplayPolicy.displayId)
                .thenReturn(MutableStateFlow(shadeDisplayId))

            val event =
                MotionEvent.obtain(500, 500, MotionEvent.ACTION_DOWN, 500f, 500f, 0).apply {
                    displayId = 0
                }

            subject.mSysUiProxy.onStatusBarTouchEvent(event)
            verify(statusBarShadeDisplayPolicy, never())
                .onStatusBarOrLauncherTouched(
                    argThat<MotionEvent> { displayId == event.displayId },
                    anyInt(),
                )
            verify(shadeViewController, never()).startExpandLatencyTracking()
        }

    @Test
    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
    @DisableFlags(Flags.FLAG_SCENE_CONTAINER, Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    fun onStatusBarTouchEvent_withoutSceneFlag_onSameDisplayTouch_handlesInput() =
        kosmos.testScope.runTest {
            val shadeDisplayId = 0
            whenever(statusBarShadeDisplayPolicy.displayId)
                .thenReturn(MutableStateFlow(shadeDisplayId))

            val event =
                MotionEvent.obtain(500, 500, MotionEvent.ACTION_DOWN, 500f, 500f, 0).apply {
                    displayId = shadeDisplayId
                }

            subject.mSysUiProxy.onStatusBarTouchEvent(event)
            verify(statusBarShadeDisplayPolicy, never())
                .onStatusBarOrLauncherTouched(
                    argThat<MotionEvent> { displayId == shadeDisplayId },
                    anyInt(),
                )
            verify(shadeViewController).startExpandLatencyTracking()
        }

    private fun createLauncherProxyService(ctx: Context): LauncherProxyService {
        return LauncherProxyService(
            ctx,
@@ -317,8 +404,9 @@ class LauncherProxyServiceTest : SysuiTestCase() {
            navModeController,
            statusBarWinController,
            kosmos.fakeSysUIStatePerDisplayRepository,
            { sceneInteractor },
            mock(),
            mock(),
            statusBarShadeDisplayPolicy,
            userTracker,
            userManager,
            wakefulnessLifecycle,
Loading