diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 625f4b85c9465..58b3de466afcb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -21,6 +21,10 @@ import static android.os.UserHandle.myUserId; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE; import android.annotation.BinderThread; import android.content.ComponentName; @@ -62,8 +66,7 @@ import java.io.PrintWriter; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -public class OneHandedController implements RemoteCallable, - OneHandedTransitionCallback { +public class OneHandedController implements RemoteCallable { private static final String TAG = "OneHandedController"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = @@ -75,7 +78,6 @@ public class OneHandedController implements RemoteCallable, private volatile boolean mIsOneHandedEnabled; private volatile boolean mIsSwipeToNotificationEnabled; - private volatile boolean mIsTransitioning; private boolean mTaskChangeToExit; private boolean mLockedDisabled; private int mUserId; @@ -89,6 +91,7 @@ public class OneHandedController implements RemoteCallable, private final OneHandedAccessibilityUtil mOneHandedAccessibilityUtil; private final OneHandedTimeoutHandler mTimeoutHandler; private final OneHandedTouchHandler mTouchHandler; + private final OneHandedState mState; private final OneHandedTutorialHandler mTutorialHandler; private final OneHandedUiEventLogger mOneHandedUiEventLogger; private final TaskStackListenerImpl mTaskStackListener; @@ -162,6 +165,19 @@ public class OneHandedController implements RemoteCallable, } }; + private final OneHandedTransitionCallback mTransitionCallBack = + new OneHandedTransitionCallback() { + @Override + public void onStartFinished(Rect bounds) { + mState.setState(STATE_ACTIVE); + } + + @Override + public void onStopFinished(Rect bounds) { + mState.setState(STATE_NONE); + } + }; + private final TaskStackListenerCallback mTaskStackListenerCallback = new TaskStackListenerCallback() { @Override @@ -200,6 +216,7 @@ public class OneHandedController implements RemoteCallable, OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil(); OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context); OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor); + OneHandedState transitionState = new OneHandedState(); OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context, windowManager, mainExecutor); OneHandedAnimationController animationController = @@ -218,7 +235,7 @@ public class OneHandedController implements RemoteCallable, ServiceManager.getService(Context.OVERLAY_SERVICE)); return new OneHandedController(context, displayController, oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler, - gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler, + gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler, transitionState, oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor, mainHandler); } @@ -234,6 +251,7 @@ public class OneHandedController implements RemoteCallable, OneHandedSettingsUtil settingsUtil, OneHandedAccessibilityUtil oneHandedAccessibilityUtil, OneHandedTimeoutHandler timeoutHandler, + OneHandedState state, OneHandedUiEventLogger uiEventsLogger, IOverlayManager overlayManager, TaskStackListenerImpl taskStackListener, @@ -246,6 +264,7 @@ public class OneHandedController implements RemoteCallable, mDisplayAreaOrganizer = displayAreaOrganizer; mDisplayController = displayController; mTouchHandler = touchHandler; + mState = state; mTutorialHandler = tutorialHandler; mGestureHandler = gestureHandler; mOverlayManager = overlayManager; @@ -330,26 +349,27 @@ public class OneHandedController implements RemoteCallable, @VisibleForTesting void startOneHanded() { - if (isLockedDisabled() || mIsTransitioning) { + if (isLockedDisabled()) { Slog.d(TAG, "Temporary lock disabled"); return; } + if (mState.isTransitioning() || mState.isInOneHanded()) { + return; + } final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation(); if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) { Slog.w(TAG, "One handed mode only support portrait mode"); return; } - if (!mDisplayAreaOrganizer.isInOneHanded()) { - mIsTransitioning = true; - final int yOffSet = Math.round( - mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); - mOneHandedAccessibilityUtil.announcementForScreenReader( - mOneHandedAccessibilityUtil.getOneHandedStartDescription()); - mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); - mTimeoutHandler.resetTimer(); - mOneHandedUiEventLogger.writeEvent( - OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); - } + mState.setState(STATE_ENTERING); + final int yOffSet = Math.round( + mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); + mOneHandedAccessibilityUtil.announcementForScreenReader( + mOneHandedAccessibilityUtil.getOneHandedStartDescription()); + mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); + mTimeoutHandler.resetTimer(); + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); } @VisibleForTesting @@ -358,14 +378,15 @@ public class OneHandedController implements RemoteCallable, } private void stopOneHanded(int uiEvent) { - if (mDisplayAreaOrganizer.isInOneHanded() && !mIsTransitioning) { - mIsTransitioning = true; - mOneHandedAccessibilityUtil.announcementForScreenReader( - mOneHandedAccessibilityUtil.getOneHandedStopDescription()); - mDisplayAreaOrganizer.scheduleOffset(0, 0); - mTimeoutHandler.removeTimer(); - mOneHandedUiEventLogger.writeEvent(uiEvent); + if (mState.isTransitioning() || mState.getState() == STATE_NONE) { + return; } + mState.setState(STATE_EXITING); + mOneHandedAccessibilityUtil.announcementForScreenReader( + mOneHandedAccessibilityUtil.getOneHandedStopDescription()); + mDisplayAreaOrganizer.scheduleOffset(0, 0); + mTimeoutHandler.removeTimer(); + mOneHandedUiEventLogger.writeEvent(uiEvent); } private void setThreeButtonModeEnabled(boolean enabled) { @@ -388,7 +409,7 @@ public class OneHandedController implements RemoteCallable, mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler); mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler); mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer); - mDisplayAreaOrganizer.registerTransitionCallback(this); + mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack); if (mTaskChangeToExit) { mTaskStackListener.addListener(mTaskStackListenerCallback); } @@ -523,8 +544,8 @@ public class OneHandedController implements RemoteCallable, } private void updateOneHandedEnabled() { - if (mDisplayAreaOrganizer.isInOneHanded()) { - stopOneHanded(); + if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) { + mMainExecutor.execute(() -> stopOneHanded()); } mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); @@ -615,8 +636,6 @@ public class OneHandedController implements RemoteCallable, pw.println(mLockedDisabled); pw.print(innerPrefix + "mUserId="); pw.println(mUserId); - pw.print(innerPrefix + "mIsTransitioning="); - pw.println(mIsTransitioning); if (mBackgroundPanelOrganizer != null) { mBackgroundPanelOrganizer.dump(pw); @@ -638,6 +657,10 @@ public class OneHandedController implements RemoteCallable, mTimeoutHandler.dump(pw); } + if (mState != null) { + mState.dump(pw); + } + if (mTutorialHandler != null) { mTutorialHandler.dump(pw); } @@ -662,26 +685,6 @@ public class OneHandedController implements RemoteCallable, } } - /** - * TODO(b/185558765) To implement a state machine for One-Handed transition state machine. - * ONE_HANDDE_STATE_TRANSITION { - * STATE_DEFAULT, - * STATE_TRANSITIONING, - * STATE_ENTER_ONE_HANED, - * STATE_EXIT_ONE_HANDED - * } - * and we need to align the state to launcher3 quick steps through SysuiProxy. - */ - @Override - public void onStartFinished(Rect bounds) { - mIsTransitioning = false; - } - - @Override - public void onStopFinished(Rect bounds) { - mIsTransitioning = false; - } - /** * The interface for calls from outside the Shell, within the host process. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index d1b3f1a2e8a54..5f3d4474687af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -65,6 +65,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private final Rect mDefaultDisplayBounds = new Rect(); private final OneHandedSettingsUtil mOneHandedSettingsUtil; + // TODO(b/185558765) Remove after OneHandedTransitionState.java merged private boolean mIsInOneHanded; private int mEnterExitAnimationDurationMs; @@ -262,6 +263,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @OneHandedAnimationController.TransitionDirection int direction) { // Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence, // the flag *MUST* be updated before dispatch mTransitionCallbacks + // TODO(b/185558765) Remove after OneHandedTransitionState.java merged mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER); mLastVisualDisplayBounds.offsetTo(0, direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0); @@ -276,6 +278,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { } /** + * TODO(b/185558765) Remove after OneHandedTransitionState.java merged * The latest state of one handed mode * * @return true Currently is in one handed mode, otherwise is not in one handed mode diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java new file mode 100644 index 0000000000000..cc874432be876 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.onehanded; + +import android.annotation.IntDef; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + Represents current OHM state by following steps, a generic CUJ is + STATE_NONE -> STATE_ENTERING -> STATE_ACTIVE -> STATE_EXITING -> STATE_NONE + */ +public class OneHandedState { + /** DEFAULT STATE after OHM feature initialized. */ + public static final int STATE_NONE = 0x00000000; + /** The state flag set when user trigger OHM. */ + public static final int STATE_ENTERING = 0x00000001; + /** The state flag set when transitioning */ + public static final int STATE_ACTIVE = 0x00000002; + /** The state flag set when user stop OHM feature. */ + public static final int STATE_EXITING = 0x00000004; + + @IntDef(prefix = { "STATE_" }, value = { + STATE_NONE, + STATE_ENTERING, + STATE_ACTIVE, + STATE_EXITING + }) + @Retention(RetentionPolicy.SOURCE) + @interface State {} + + public OneHandedState() { + sCurrentState = STATE_NONE; + } + + @State + private static int sCurrentState = STATE_NONE; + + private static final String TAG = OneHandedState.class.getSimpleName(); + + /** + * Gets current transition state of One handed mode. + * + * @return The bitwise flags representing current states. + */ + public @State int getState() { + return sCurrentState; + } + + /** + * Is the One handed mode is in transitioning state. + * @return true if One handed mode is in transitioning states. + */ + public boolean isTransitioning() { + return sCurrentState == STATE_ENTERING || sCurrentState == STATE_EXITING; + } + + /** + * Is the One handed mode active state. + * @return true if One handed mode is active state. + */ + public boolean isInOneHanded() { + return sCurrentState == STATE_ACTIVE; + } + + /** + * Sets new state for One handed mode feature. + * @param newState The bitwise value to represent current transition states. + */ + public void setState(@State int newState) { + sCurrentState = newState; + } + + /** Dumps internal state. */ + public void dump(PrintWriter pw) { + final String innerPrefix = " "; + pw.println(TAG + "states: "); + pw.println(innerPrefix + "sCurrentState=" + sCurrentState); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 105bd828aa9e5..c1282c9d7b125 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -16,6 +16,9 @@ package com.android.wm.shell.onehanded; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -61,6 +64,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { OneHandedAccessibilityUtil mOneHandedAccessibilityUtil; OneHandedController mSpiedOneHandedController; OneHandedTimeoutHandler mSpiedTimeoutHandler; + OneHandedState mSpiedTransitionState; @Mock DisplayController mMockDisplayController; @@ -99,9 +103,9 @@ public class OneHandedControllerTest extends OneHandedTestCase { mDisplay = mContext.getDisplay(); mDisplayLayout = new DisplayLayout(mContext, mDisplay); mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); + mSpiedTransitionState = spy(new OneHandedState()); when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( @@ -129,6 +133,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mMockSettingsUitl, mOneHandedAccessibilityUtil, mSpiedTimeoutHandler, + mSpiedTransitionState, mMockUiEventLogger, mMockOverlayManager, mMockTaskStackListener, @@ -139,18 +144,13 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testDefaultShouldNotInOneHanded() { - final OneHandedAnimationController animationController = new OneHandedAnimationController( - mContext); - OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer( - mContext, mDisplayLayout, mMockSettingsUitl, animationController, - mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor); - - assertThat(displayAreaOrganizer.isInOneHanded()).isFalse(); + // Assert default transition state is STATE_NONE + assertThat(mSpiedTransitionState.getState()).isEqualTo(STATE_NONE); } @Test public void testStartOneHandedShouldTriggerScheduleOffset() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.startOneHanded(); @@ -160,7 +160,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testStartOneHandedShouldNotTriggerScheduleOffset() { mSpiedOneHandedController.setOneHandedEnabled(true); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true); + mSpiedTransitionState.setState(STATE_ENTERING); mSpiedOneHandedController.startOneHanded(); verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); @@ -168,7 +168,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testStopOneHanded() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.stopOneHanded(); verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); @@ -192,7 +192,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testStopOneHandedShouldRemoveTimer() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true); + mSpiedTransitionState.setState(STATE_ENTERING); mSpiedOneHandedController.stopOneHanded(); verify(mSpiedTimeoutHandler, atLeastOnce()).removeTimer(); @@ -280,7 +280,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testKeyguardShowingLockOneHandedDisabled() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(true /* locked */, false /* enabled */); mSpiedOneHandedController.startOneHanded(); @@ -290,7 +290,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testResetKeyguardShowingLockOneHandedDisabled() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); mSpiedOneHandedController.startOneHanded(); @@ -302,7 +302,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testRotation90CanNotStartOneHanded() { final DisplayLayout landscapeDisplayLayout = new DisplayLayout(mDisplayLayout); landscapeDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(landscapeDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); @@ -315,7 +315,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testRotation180CanStartOneHanded() { final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout); testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); @@ -328,7 +328,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testRotation270CanNotStartOneHanded() { final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout); testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java new file mode 100644 index 0000000000000..89aae652444f9 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.onehanded; + +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.om.IOverlayManager; +import android.graphics.Rect; +import android.os.Handler; +import android.os.UserHandle; +import android.testing.AndroidTestingRunner; +import android.util.ArrayMap; +import android.view.Display; +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TaskStackListenerImpl; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class OneHandedStateTest extends OneHandedTestCase { + private int mCurrentUser = UserHandle.myUserId(); + + Display mDisplay; + DisplayLayout mDisplayLayout; + OneHandedAccessibilityUtil mOneHandedAccessibilityUtil; + OneHandedController mSpiedOneHandedController; + OneHandedTimeoutHandler mSpiedTimeoutHandler; + OneHandedState mSpiedState; + + @Mock + DisplayController mMockDisplayController; + @Mock + OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer; + @Mock + OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; + @Mock + OneHandedTouchHandler mMockTouchHandler; + @Mock + OneHandedTutorialHandler mMockTutorialHandler; + @Mock + OneHandedGestureHandler mMockGestureHandler; + @Mock + OneHandedSettingsUtil mMockSettingsUitl; + @Mock + OneHandedUiEventLogger mMockUiEventLogger; + @Mock + IOverlayManager mMockOverlayManager; + @Mock + TaskStackListenerImpl mMockTaskStackListener; + @Mock + ShellExecutor mMockShellMainExecutor; + @Mock + SurfaceControl mMockLeash; + @Mock + Handler mMockShellMainHandler; + + final boolean mDefaultEnabled = true; + final boolean mDefaultSwipeToNotificationEnabled = false; + final boolean mDefaultTapAppToExitEnabled = true; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mDisplay = mContext.getDisplay(); + mDisplayLayout = new DisplayLayout(mContext, mDisplay); + mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); + mSpiedState = spy(new OneHandedState()); + + when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); + when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); + when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); + when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( + mDefaultEnabled); + when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn( + OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + when(mMockSettingsUitl.getSettingsTapsAppToExit(any(), anyInt())).thenReturn( + mDefaultTapAppToExitEnabled); + when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( + mDefaultSwipeToNotificationEnabled); + + when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn( + new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height())); + when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout); + + mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext); + mSpiedOneHandedController = spy(new OneHandedController( + mContext, + mMockDisplayController, + mMockBackgroundOrganizer, + mMockDisplayAreaOrganizer, + mMockTouchHandler, + mMockTutorialHandler, + mMockGestureHandler, + mMockSettingsUitl, + mOneHandedAccessibilityUtil, + mSpiedTimeoutHandler, + mSpiedState, + mMockUiEventLogger, + mMockOverlayManager, + mMockTaskStackListener, + mMockShellMainExecutor, + mMockShellMainHandler) + ); + } + + @Test + public void testState_stateEntering_isTransitioning() { + mSpiedState.setState(STATE_ENTERING); + + assertThat(mSpiedState.isTransitioning()).isTrue(); + } + + @Test + public void testState_stateExiting_isTransitioning() { + mSpiedState.setState(STATE_EXITING); + + assertThat(mSpiedState.isTransitioning()).isTrue(); + } + + @Test + public void testInEnteringState_shouldSkipDupTrigger() { + when(mSpiedState.getState()).thenReturn(STATE_ENTERING); + when(mSpiedState.isTransitioning()).thenReturn(true); + when(mSpiedState.isInOneHanded()).thenReturn(false); + mSpiedOneHandedController.startOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInActiveState_shouldSkipDupTrigger() { + when(mSpiedState.getState()).thenReturn(STATE_ACTIVE); + when(mSpiedState.isTransitioning()).thenReturn(false); + when(mSpiedState.isInOneHanded()).thenReturn(true); + mSpiedOneHandedController.startOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInActiveState_canExit() { + when(mSpiedState.getState()).thenReturn(STATE_ACTIVE); + when(mSpiedState.isTransitioning()).thenReturn(false); + mSpiedOneHandedController.stopOneHanded(); + + verify(mSpiedState).setState(STATE_EXITING); + } + + @Test + public void testInEnteringState_shouldSkipExitAction() { + when(mSpiedState.getState()).thenReturn(STATE_ENTERING); + when(mSpiedState.isTransitioning()).thenReturn(true); + mSpiedOneHandedController.stopOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInExitingState_shouldSkipStartAction() { + when(mSpiedState.getState()).thenReturn(STATE_EXITING); + when(mSpiedState.isTransitioning()).thenReturn(true); + mSpiedOneHandedController.startOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInExitingState_shouldSkipStopAction() { + when(mSpiedState.getState()).thenReturn(STATE_EXITING); + when(mSpiedState.isTransitioning()).thenReturn(true); + mSpiedOneHandedController.stopOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInActiveState_disableOHM_shouldStopOHM() { + when(mSpiedState.getState()).thenReturn(STATE_ACTIVE); + when(mSpiedState.isTransitioning()).thenReturn(false); + when(mSpiedState.isInOneHanded()).thenReturn(true); + mSpiedOneHandedController.setOneHandedEnabled(false); + + verify(mMockShellMainExecutor).execute(any()); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index 06a66717ed317..b82a8ca4ffa06 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -41,6 +41,7 @@ import org.mockito.MockitoAnnotations; public class OneHandedTutorialHandlerTest extends OneHandedTestCase { OneHandedTimeoutHandler mTimeoutHandler; OneHandedController mOneHandedController; + OneHandedState mSpiedTransitionState; @Mock OneHandedGestureHandler mMockGestureHandler; @@ -73,6 +74,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { public void setUp() { MockitoAnnotations.initMocks(this); mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor); + mSpiedTransitionState = new OneHandedState(); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); mOneHandedController = new OneHandedController( @@ -86,6 +88,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mMockSettingsUtil, mMockAccessibilityUtil, mTimeoutHandler, + mSpiedTransitionState, mMockUiEventLogger, mMockOverlayManager, mMockTaskStackListener,