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

Commit bb4db1a7 authored by Chen Bai's avatar Chen Bai Committed by Android (Google) Code Review
Browse files

Merge "clobber wom: move onKeyUp focused task on top for triple stem A11y" into main

parents 944cbe1b c447ffd8
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -76,6 +76,18 @@ class DeferredKeyActionExecutor {
        getActionsBufferWithLazyCleanUp(keyCode, downTime).setExecutable();
    }

    /**
     * Clears all the queued action for given key code.
     *
     * @param keyCode the key code whose queued actions will be cleared.
     */
    public void cancelQueuedAction(int keyCode) {
        TimedActionsBuffer actionsBuffer = mBuffers.get(keyCode);
        if (actionsBuffer != null) {
            actionsBuffer.clear();
        }
    }

    private TimedActionsBuffer getActionsBufferWithLazyCleanUp(int keyCode, long downTime) {
        TimedActionsBuffer buffer = mBuffers.get(keyCode);
        if (buffer == null || buffer.getDownTime() != downTime) {
@@ -146,6 +158,10 @@ class DeferredKeyActionExecutor {
            mActions.clear();
        }

        void clear() {
            mActions.clear();
        }

        void dump(String prefix, PrintWriter pw) {
            if (mExecutable) {
                pw.println(prefix + "  " + KeyEvent.keyCodeToString(mKeyCode) + ": executable");
+71 −8
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IUiModeManager;
@@ -584,6 +585,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    private int mLongPressOnStemPrimaryBehavior;
    private RecentTaskInfo mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;

    // The focused task at the time when the first STEM_PRIMARY key was released. This can only
    // be accessed from the looper thread.
    private RootTaskInfo mFocusedTaskInfoOnStemPrimarySingleKeyUp;

    private boolean mHandleVolumeKeysInWM;

    private boolean mPendingKeyguardOccluded;
@@ -2135,12 +2140,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    static class Injector {
        private final Context mContext;
        private final WindowManagerFuncs mWindowManagerFuncs;
        private final Looper mLooper;

        Injector(Context context, WindowManagerFuncs funcs, Looper looper) {
        Injector(Context context, WindowManagerFuncs funcs) {
            mContext = context;
            mWindowManagerFuncs = funcs;
            mLooper = looper;
        }

        Context getContext() {
@@ -2152,7 +2155,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }

        Looper getLooper() {
            return mLooper;
            return Looper.myLooper();
        }

        AccessibilityShortcutController getAccessibilityShortcutController(
@@ -2195,7 +2198,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    /** {@inheritDoc} */
    @Override
    public void init(Context context, WindowManagerFuncs funcs) {
        init(new Injector(context, funcs, Looper.myLooper()));
        init(new Injector(context, funcs));
    }

    @VisibleForTesting
@@ -2723,7 +2726,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {

        @Override
        void onPress(long downTime, int unusedDisplayId) {
            if (mShouldEarlyShortPressOnStemPrimary) {
            if (shouldHandleStemPrimaryEarlyShortPress()) {
                return;
            }
            // Short-press should be triggered only if app doesn't handle it.
@@ -2747,6 +2750,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            if (count == 3
                    && mTriplePressOnStemPrimaryBehavior
                    == TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY) {
                // Cancel any queued actions for current key code to prevent them from being
                // launched after a11y layer enabled. If the action happens early,
                // undoEarlySinglePress will make sure the correct task is on top.
                mDeferredKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY);
                undoEarlySinglePress();
                stemPrimaryPress(count);
            } else {
                // Other multi-press gestures should be triggered only if app doesn't handle it.
@@ -2755,6 +2763,27 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            }
        }

        /**
         * This method undo the previously launched early-single-press action by bringing the
         * focused task before launching early-single-press back to top.
         */
        private void undoEarlySinglePress() {
            if (shouldHandleStemPrimaryEarlyShortPress()
                    && mFocusedTaskInfoOnStemPrimarySingleKeyUp != null) {
                try {
                    mActivityManagerService.startActivityFromRecents(
                            mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId, null);
                } catch (RemoteException | IllegalArgumentException e) {
                    Slog.e(
                            TAG,
                            "Failed to start task "
                                    + mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId
                                    + " from recents",
                            e);
                }
            }
        }

        @Override
        void onKeyUp(long eventTime, int count, int unusedDisplayId) {
            if (count == 1) {
@@ -2763,14 +2792,48 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                // It is possible that we may navigate away from this task before the double
                // press is detected, as a result of the first press, so we save the  current
                // most recent task before that happens.
                // TODO(b/311497918): guard this with DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP
                mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp =
                        mActivityTaskManagerInternal.getMostRecentTaskFromBackground();
                if (mShouldEarlyShortPressOnStemPrimary) {

                mFocusedTaskInfoOnStemPrimarySingleKeyUp = null;

                if (shouldHandleStemPrimaryEarlyShortPress()) {
                    // Key-up gesture should be triggered only if app doesn't handle it.
                    mDeferredKeyActionExecutor.queueKeyAction(
                            KeyEvent.KEYCODE_STEM_PRIMARY, eventTime, () -> stemPrimaryPress(1));
                            KeyEvent.KEYCODE_STEM_PRIMARY,
                            eventTime,
                            () -> {
                                // Save the info of the focused task on screen. This may be used
                                // later to bring the current focused task back to top. For
                                // example, stem primary triple press enables the A11y interface
                                // on top of the current focused task. When early single press is
                                // enabled for stem primary, the focused task could change to
                                // something else upon first key up event. In that case, we will
                                // bring the task recorded by this variable back to top. Then, start
                                // A11y interface.
                                try {
                                    mFocusedTaskInfoOnStemPrimarySingleKeyUp =
                                            mActivityManagerService.getFocusedRootTaskInfo();
                                } catch (RemoteException e) {
                                    Slog.e(
                                            TAG,
                                            "StemPrimaryKeyRule: onKeyUp: error while getting "
                                                    + "focused task "
                                                    + "info.",
                                            e);
                                }

                                stemPrimaryPress(1);
                            });
                }
            }
        }

        // TODO(b/311497918): make a shouldHandlePowerEarlyShortPress for power button.
        private boolean shouldHandleStemPrimaryEarlyShortPress() {
            return mShouldEarlyShortPressOnStemPrimary
                    && mShortPressOnStemPrimaryBehavior == SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
        }
    }

+20 −0
Original line number Diff line number Diff line
@@ -95,6 +95,26 @@ public final class DeferredKeyActionExecutorTests {
        assertFalse(action.executed);
    }

    @Test
    public void queueKeyAction_beforeAndAfterCancelQueuedActions_onlyActionsAfterCancelExecuted() {
        TestAction action1 = new TestAction();
        TestAction action2 = new TestAction();
        TestAction action3 = new TestAction();
        mKeyActionExecutor.queueKeyAction(
                KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action1);
        mKeyActionExecutor.queueKeyAction(
                KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action2);
        mKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY);
        mKeyActionExecutor.queueKeyAction(
                KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action3);

        mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1);

        assertFalse(action1.executed);
        assertFalse(action2.executed);
        assertTrue(action3.executed);
    }

    static class TestAction implements Runnable {
        public boolean executed;

+56 −5
Original line number Diff line number Diff line
@@ -19,14 +19,18 @@ package com.android.server.policy;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.policy.PhoneWindowManager.DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY;
import static com.android.server.policy.PhoneWindowManager.TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY;

import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.content.ComponentName;
import android.os.RemoteException;
import android.provider.Settings;
@@ -50,6 +54,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
    public void stemSingleKey_duringSetup_doNothing() {
        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
        mPhoneWindowManager.overrideIsUserSetupComplete(false);

@@ -65,6 +70,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
    public void stemSingleKey_AfterSetup_openAllApp() {
        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
        mPhoneWindowManager.overrideStartActivity();
        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
        mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -83,6 +89,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
                STEM_PRIMARY_BUTTON_SHORT_PRESS,
                SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
        mPhoneWindowManager.overrideStartActivity();
        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
        mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -104,6 +111,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
        mPhoneWindowManager.overrideIsUserSetupComplete(true);
        mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true);

        setDispatchedKeyHandler(keyEvent -> true);

        sendKey(KEYCODE_STEM_PRIMARY);
@@ -131,6 +139,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
                STEM_PRIMARY_BUTTON_LONG_PRESS,
                LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
        mPhoneWindowManager.setupAssistForLaunch();
        mPhoneWindowManager.overrideIsUserSetupComplete(true);

@@ -144,6 +153,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
                STEM_PRIMARY_BUTTON_LONG_PRESS,
                LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
        mPhoneWindowManager.setupAssistForLaunch();
        mPhoneWindowManager.overrideSearchManager(null);
        mPhoneWindowManager.overrideStatusBarManagerInternal();
@@ -156,7 +166,8 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
    @Test
    public void stemDoubleKey_EarlyShortPress_AllAppsThenSwitchToMostRecent()
            throws RemoteException {
        overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
        overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true);
        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
@@ -171,14 +182,47 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
        sendKey(KEYCODE_STEM_PRIMARY);

        mPhoneWindowManager.assertOpenAllAppView();
        mPhoneWindowManager.assertSwitchToRecent(referenceId);
        mPhoneWindowManager.assertSwitchToTask(referenceId);
    }

    @Test
    public void stemDoubleKey_NoEarlyShortPress_SwitchToMostRecent() throws RemoteException {
    public void stemTripleKey_EarlyShortPress_AllAppsThenBackToOriginalThenToggleA11y()
            throws RemoteException {
        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
        overrideBehavior(
                STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true);
        mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true);
        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
        mPhoneWindowManager.overrideIsUserSetupComplete(true);
        RootTaskInfo allAppsTask = new RootTaskInfo();
        int referenceId = 777;
        allAppsTask.taskId = referenceId;
        doReturn(allAppsTask)
                .when(mPhoneWindowManager.mActivityManagerService)
                .getFocusedRootTaskInfo();

        mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ false);

        sendKey(KEYCODE_STEM_PRIMARY);
        sendKey(KEYCODE_STEM_PRIMARY);
        sendKey(KEYCODE_STEM_PRIMARY);

        mPhoneWindowManager.assertOpenAllAppView();
        mPhoneWindowManager.assertSwitchToTask(referenceId);
        mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true);
    }

    @Test
    public void stemMultiKey_NoEarlyPress_NoOpenAllApp() throws RemoteException {
        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
        overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
        overrideBehavior(
                STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY);
        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
        mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
        mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true);
        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
        mPhoneWindowManager.overrideIsUserSetupComplete(true);
        RecentTaskInfo recentTaskInfo = new RecentTaskInfo();
@@ -187,11 +231,18 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
        doReturn(recentTaskInfo).when(
                mPhoneWindowManager.mActivityTaskManagerInternal).getMostRecentTaskFromBackground();

        sendKey(KEYCODE_STEM_PRIMARY);
        sendKey(KEYCODE_STEM_PRIMARY);
        sendKey(KEYCODE_STEM_PRIMARY);

        mPhoneWindowManager.assertNotOpenAllAppView();
        mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true);

        sendKey(KEYCODE_STEM_PRIMARY);
        sendKey(KEYCODE_STEM_PRIMARY);

        mPhoneWindowManager.assertNotOpenAllAppView();
        mPhoneWindowManager.assertSwitchToRecent(referenceId);
        mPhoneWindowManager.assertSwitchToTask(referenceId);
    }

    @Test
@@ -215,7 +266,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase {
        sendKey(KEYCODE_STEM_PRIMARY);

        mPhoneWindowManager.assertNotOpenAllAppView();
        mPhoneWindowManager.assertSwitchToRecent(referenceId);
        mPhoneWindowManager.assertSwitchToTask(referenceId);
    }

    private void overrideBehavior(String key, int expectedBehavior) {
+22 −6
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import android.media.AudioManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
@@ -176,8 +177,9 @@ class TestPhoneWindowManager {
    private Handler mHandler;

    private boolean mIsTalkBackEnabled;
    private boolean mIsTalkBackShortcutGestureEnabled;

    class TestTalkbackShortcutController extends TalkbackShortcutController {
    private class TestTalkbackShortcutController extends TalkbackShortcutController {
        TestTalkbackShortcutController(Context context) {
            super(context);
        }
@@ -190,13 +192,18 @@ class TestPhoneWindowManager {

        @Override
        boolean isTalkBackShortcutGestureEnabled() {
            return true;
            return mIsTalkBackShortcutGestureEnabled;
        }
    }

    private class TestInjector extends PhoneWindowManager.Injector {
        TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
            super(context, funcs, mTestLooper.getLooper());
            super(context, funcs);
        }

        @Override
        Looper getLooper() {
            return mTestLooper.getLooper();
        }

        AccessibilityShortcutController getAccessibilityShortcutController(
@@ -410,6 +417,10 @@ class TestPhoneWindowManager {
        mPhoneWindowManager.mShouldEarlyShortPressOnStemPrimary = shouldEarlyShortPress;
    }

    void overrideTalkbackShortcutGestureEnabled(boolean enabled) {
        mIsTalkBackShortcutGestureEnabled = enabled;
    }

     // Override assist perform function.
    void overrideLongPressOnPower(int behavior) {
        mPhoneWindowManager.mLongPressOnPowerBehavior = behavior;
@@ -714,7 +725,7 @@ class TestPhoneWindowManager {
    }

    void assertOpenAllAppView() {
        mTestLooper.dispatchAll();
        moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS);
        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
                .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -728,7 +739,7 @@ class TestPhoneWindowManager {
    }

    void assertActivityTargetLaunched(ComponentName targetActivity) {
        mTestLooper.dispatchAll();
        moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS);
        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
                .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -743,10 +754,15 @@ class TestPhoneWindowManager {
                        expectedModifierState, deviceBus), description(errorMsg));
    }

    void assertSwitchToRecent(int persistentId) throws RemoteException {
    void assertSwitchToTask(int persistentId) throws RemoteException {
        mTestLooper.dispatchAll();
        verify(mActivityManagerService,
                timeout(TEST_SINGLE_KEY_DELAY_MILLIS)).startActivityFromRecents(eq(persistentId),
                isNull());
    }

    void assertTalkBack(boolean expectEnabled) {
        mTestLooper.dispatchAll();
        Assert.assertEquals(expectEnabled, mIsTalkBackEnabled);
    }
}