From eb9b719bc5d8119cf027165cf88a8954efc96b5b Mon Sep 17 00:00:00 2001 From: Bill Lin Date: Fri, 23 Apr 2021 15:43:17 +0800 Subject: [PATCH] 3/ Listen ONE_HANDED_MODE_ACTIVATED for A11y shortcut action - Observe ONE_HANDED_MODE_ACTIVATED secure settings key - Skip action when OneHandedState is in transition 1. STATE_ENTERING 2. STATE_EXITING - Skip action if current OneHandedState = request activate 1. STATE NONE -> Shortcut request ACTIVATED : StartOneHanded 2. STATE_ACTIVE -> Shortcut request non ACTIVATED : StopOneHanded 3. STATE_NONE -> Shortcut request non ACTIVATED : SKip 4. STATE_ACTIVE -> Shortcut request ACTIVATED : Skip - Controller will update latest state to secure setting ONE_HANDED_MODE_ACTIVATED either trigger by gesture or shortcut Test: atest WMShellUnitTests Test: enable shortcut and click Bug: 182425480 Change-Id: I03bd2679a5a08801efc9868148adfddef87171f4 --- .../shell/onehanded/OneHandedController.java | 38 ++++++++++ .../onehanded/OneHandedSettingsUtil.java | 26 ++++++- .../onehanded/OneHandedControllerTest.java | 69 +++++++++++++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) 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 04ec3917428e4..575e5c75c1562 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 @@ -135,6 +135,7 @@ public class OneHandedController implements RemoteCallable } }; + private final ContentObserver mActivatedObserver; private final ContentObserver mEnabledObserver; private final ContentObserver mTimeoutObserver; private final ContentObserver mTaskChangeExitObserver; @@ -170,11 +171,13 @@ public class OneHandedController implements RemoteCallable @Override public void onStartFinished(Rect bounds) { mState.setState(STATE_ACTIVE); + notifyShortcutState(STATE_ACTIVE); } @Override public void onStopFinished(Rect bounds) { mState.setState(STATE_NONE); + notifyShortcutState(STATE_NONE); } }; @@ -287,6 +290,7 @@ public class OneHandedController implements RemoteCallable context.getContentResolver(), mUserId); mTimeoutHandler = timeoutHandler; + mActivatedObserver = getObserver(this::onActivatedActionChanged); mEnabledObserver = getObserver(this::onEnabledSettingChanged); mTimeoutObserver = getObserver(this::onTimeoutSettingChanged); mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged); @@ -347,6 +351,12 @@ public class OneHandedController implements RemoteCallable updateOneHandedEnabled(); } + @VisibleForTesting + void notifyShortcutState(@OneHandedState.State int state) { + mOneHandedSettingsUtil.setOneHandedModeActivated( + mContext.getContentResolver(), state == STATE_ACTIVE ? 1 : 0, mUserId); + } + @VisibleForTesting void startOneHanded() { if (isLockedDisabled()) { @@ -416,6 +426,9 @@ public class OneHandedController implements RemoteCallable } private void registerSettingObservers(int newUserId) { + mOneHandedSettingsUtil.registerSettingsKeyObserver( + Settings.Secure.ONE_HANDED_MODE_ACTIVATED, + mContext.getContentResolver(), mActivatedObserver, newUserId); mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED, mContext.getContentResolver(), mEnabledObserver, newUserId); mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT, @@ -465,6 +478,26 @@ public class OneHandedController implements RemoteCallable }; } + @VisibleForTesting + void onActivatedActionChanged() { + if (mState.isTransitioning() || !isOneHandedEnabled()) { + return; + } + + final boolean isActivated = mState.getState() == STATE_ACTIVE; + final boolean requestActivated = mOneHandedSettingsUtil.getOneHandedModeActivated( + mContext.getContentResolver(), mUserId); + // When gesture trigger action, we will update settings and introduce observer callback + // again, then the following logic will just ignore the second redundant callback. + if (isActivated ^ requestActivated) { + if (requestActivated) { + startOneHanded(); + } else { + stopOneHanded(); + } + } + } + @VisibleForTesting void onEnabledSettingChanged() { final boolean enabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( @@ -544,6 +577,11 @@ public class OneHandedController implements RemoteCallable return mLockedDisabled; } + @VisibleForTesting + boolean isOneHandedEnabled() { + return mIsOneHandedEnabled; + } + private void updateOneHandedEnabled() { if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) { mMainExecutor.execute(() -> stopOneHanded()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java index 1b2fcdd6313e6..232b6f9cfdd81 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java @@ -131,8 +131,28 @@ public final class OneHandedSettingsUtil { * Returns whether swipe bottom to notification gesture enabled or not. */ public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver, int userId) { - return Settings.Secure.getInt(resolver, - Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0 /* Default OFF */) == 1; + return Settings.Secure.getIntForUser(resolver, + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0, userId) == 1; + } + + /** + * Sets one handed activated or not to notify state for shortcut + * + * @return activated or not + */ + public boolean getOneHandedModeActivated(ContentResolver resolver, int userId) { + return Settings.Secure.getIntForUser(resolver, + Settings.Secure.ONE_HANDED_MODE_ACTIVATED, 0, userId) == 1; + } + + /** + * Sets one handed activated or not to notify state for shortcut + * + * @return activated or not + */ + public boolean setOneHandedModeActivated(ContentResolver resolver, int state, int userId) { + return Settings.Secure.putIntForUser(resolver, + Settings.Secure.ONE_HANDED_MODE_ACTIVATED, state, userId); } void dump(PrintWriter pw, String prefix, ContentResolver resolver, @@ -145,6 +165,8 @@ public final class OneHandedSettingsUtil { pw.println(getSettingsOneHandedModeTimeout(resolver, userId)); pw.print(innerPrefix + "tapsAppToExit="); pw.println(getSettingsTapsAppToExit(resolver, userId)); + pw.print(innerPrefix + "shortcutActivated="); + pw.println(getOneHandedModeActivated(resolver, userId)); } public OneHandedSettingsUtil() { 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 c1282c9d7b125..99342f02463af 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,7 +16,9 @@ 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.android.wm.shell.onehanded.OneHandedState.STATE_NONE; import static com.google.common.truth.Truth.assertThat; @@ -360,4 +362,71 @@ public class OneHandedControllerTest extends OneHandedTestCase { verify(mMockGestureHandler).onGestureEnabled(isOneHandedEnabled); } + + @Test + public void testStateActive_shortcutRequestActivate_skipActions() { + when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mSpiedOneHandedController, never()).startOneHanded(); + verify(mSpiedOneHandedController, never()).stopOneHanded(); + } + + @Test + public void testStateNotActive_shortcutRequestInActivate_skipAction() { + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mSpiedOneHandedController, never()).startOneHanded(); + verify(mSpiedOneHandedController, never()).stopOneHanded(); + } + + @Test + public void testStateNotActive_shortcutRequestActivate_doAction() { + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mSpiedOneHandedController).startOneHanded(); + verify(mSpiedOneHandedController, never()).stopOneHanded(); + } + + @Test + public void testEnteringTransition_shortcutRequestActivate_skipActions() { + when(mSpiedTransitionState.getState()).thenReturn(STATE_ENTERING); + when(mSpiedTransitionState.isTransitioning()).thenReturn(true); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mSpiedOneHandedController, never()).startOneHanded(); + verify(mSpiedOneHandedController, never()).stopOneHanded(); + } + + @Test + public void testExitingTransition_shortcutRequestActivate_skipActions() { + when(mSpiedTransitionState.getState()).thenReturn(STATE_EXITING); + when(mSpiedTransitionState.isTransitioning()).thenReturn(true); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mSpiedOneHandedController, never()).startOneHanded(); + verify(mSpiedOneHandedController, never()).stopOneHanded(); + } + + @Test + public void testOneHandedDisabled_shortcutEnabled_skipActions() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mSpiedOneHandedController, never()).startOneHanded(); + verify(mSpiedOneHandedController, never()).stopOneHanded(); + } }