Merge "Nullability Annotations replacement" into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index f794fef..893a87f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -48,12 +48,14 @@
private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100;
+ private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
+ private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
+ private static final int EXPANDED_VIEW_DRAG_ANIMATION_DURATION = 150;
/**
* Additional scale applied to expanded view when it is positioned inside a magnetic target.
*/
- private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f;
- private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
- private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
+ private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.6f;
+ private static final float EXPANDED_VIEW_DRAG_SCALE = 0.5f;
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -72,6 +74,7 @@
private final Context mContext;
private final BubbleBarLayerView mLayerView;
private final BubblePositioner mPositioner;
+ private final int[] mTmpLocation = new int[2];
private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded = false;
@@ -220,6 +223,25 @@
}
/**
+ * Animate the expanded bubble when it is being dragged
+ */
+ public void animateStartDrag() {
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ Log.w(TAG, "Trying to animate start drag without a bubble");
+ return;
+ }
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(0f);
+ bbev.animate()
+ .scaleX(EXPANDED_VIEW_DRAG_SCALE)
+ .scaleY(EXPANDED_VIEW_DRAG_SCALE)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION)
+ .start();
+ }
+
+ /**
* Animates dismissal of currently expanded bubble
*
* @param endRunnable a runnable to run at the end of the animation
@@ -261,7 +283,10 @@
.setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION)
.setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
.withStartAction(() -> bbev.setAnimating(true))
- .withEndAction(() -> bbev.setAnimating(false))
+ .withEndAction(() -> {
+ bbev.setAnimating(false);
+ bbev.resetPivot();
+ })
.start();
}
@@ -277,25 +302,52 @@
Log.w(TAG, "Trying to snap the expanded view to target without a bubble");
return;
}
- Point expandedViewCenter = getViewCenterOnScreen(bbev);
-
- // Calculate the difference between the target's center coordinates and the object's.
- // Animating the object's x/y properties by these values will center the object on top
- // of the magnetic target.
- float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x;
- float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y;
// Calculate scale of expanded view so it fits inside the magnetic target
float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight());
- float targetMaxSide = Math.max(target.getTargetView().getWidth(),
- target.getTargetView().getHeight());
- float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide;
+ View targetView = target.getTargetView();
+ float targetMaxSide = Math.max(targetView.getWidth(), targetView.getHeight());
+ // Reduce target size to have some padding between the target and expanded view
+ targetMaxSide *= EXPANDED_VIEW_IN_TARGET_SCALE;
+ float scaleInTarget = targetMaxSide / bbevMaxSide;
+
+ // Scale around the top center of the expanded view. Same as when dragging.
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(0);
+
+ // When the view animates into the target, it is scaled down with the pivot at center top.
+ // Find the point on the view that would be the center of the view at its final scale.
+ // Once we know that, we can calculate x and y distance from the center of the target view
+ // and use that for the translation animation to ensure that the view at final scale is
+ // placed at the center of the target.
+
+ // Set mTmpLocation to the current location of the view on the screen, taking into account
+ // any scale applied.
+ bbev.getLocationOnScreen(mTmpLocation);
+ // Since pivotX is at the center of the x-axis, even at final scale, center of the view on
+ // x-axis will be the same as the center of the view at current size.
+ // Get scaled width of the view and adjust mTmpLocation so that point on x-axis is at the
+ // center of the view at its current size.
+ float currentWidth = bbev.getWidth() * bbev.getScaleX();
+ mTmpLocation[0] += currentWidth / 2;
+ // Since pivotY is at the top of the view, at final scale, top coordinate of the view
+ // remains the same.
+ // Get height of the view at final scale and adjust mTmpLocation so that point on y-axis is
+ // moved down by half of the height at final scale.
+ float targetHeight = bbev.getHeight() * scaleInTarget;
+ mTmpLocation[1] += targetHeight / 2;
+ // mTmpLocation is now set to the point on the view that will be the center of the view once
+ // scale is applied.
+
+ // Calculate the difference between the target's center coordinates and mTmpLocation
+ float xDiff = target.getCenterOnScreen().x - mTmpLocation[0];
+ float yDiff = target.getCenterOnScreen().y - mTmpLocation[1];
bbev.animate()
.translationX(bbev.getTranslationX() + xDiff)
.translationY(bbev.getTranslationY() + yDiff)
- .scaleX(scale)
- .scaleY(scale)
+ .scaleX(scaleInTarget)
+ .scaleY(scaleInTarget)
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
.setInterpolator(Interpolators.EMPHASIZED)
.withStartAction(() -> bbev.setAnimating(true))
@@ -319,8 +371,8 @@
}
expandedView
.animate()
- .scaleX(1f)
- .scaleY(1f)
+ .scaleX(EXPANDED_VIEW_DRAG_SCALE)
+ .scaleY(EXPANDED_VIEW_DRAG_SCALE)
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
.setInterpolator(Interpolators.EMPHASIZED)
.withStartAction(() -> expandedView.setAnimating(true))
@@ -385,12 +437,4 @@
final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
return new Size(width, height);
}
-
- private Point getViewCenterOnScreen(View view) {
- Point center = new Point();
- int[] onScreenLocation = view.getLocationOnScreen();
- center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f));
- center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f));
- return center;
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index d215450..5e634a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -74,6 +74,9 @@
}
private inner class HandleDragListener : RelativeTouchListener() {
+
+ private var isMoving = false
+
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
return !expandedView.isAnimating
@@ -87,6 +90,10 @@
dx: Float,
dy: Float
) {
+ if (!isMoving) {
+ isMoving = true
+ animationHelper.animateStartDrag()
+ }
expandedView.translationX = expandedViewInitialTranslationX + dx
expandedView.translationY = expandedViewInitialTranslationY + dy
dismissView.show()
@@ -114,6 +121,7 @@
animationHelper.animateToRestPosition()
dismissView.hide()
}
+ isMoving = false
}
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index b4c3850..782b850 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -268,3 +268,11 @@
description: "Adds haptic feedback to the volume slider."
bug: "316953430"
}
+
+flag {
+ name: "screenshare_notification_hiding"
+ namespace: "systemui"
+ description: "Enable hiding of notifications during screenshare"
+ bug: "312784809"
+}
+
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 55d8a0f..8e79922a 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -41,3 +41,12 @@
description: "This flag controls the fix for notifications autogroup summary icon updates"
bug: "227693160"
}
+
+flag {
+ name: "sensitive_notification_app_protection"
+ namespace: "systemui"
+ description: "This flag controls the sensitive notification app protections while screen sharing"
+ bug: "312784351"
+ # Referenced in WM where WM starts before DeviceConfig
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
index b531b0e..611e4ed 100644
--- a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
+++ b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
@@ -76,6 +76,18 @@
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 @@
mActions.clear();
}
+ void clear() {
+ mActions.clear();
+ }
+
void dump(String prefix, PrintWriter pw) {
if (mExecutable) {
pw.println(prefix + " " + KeyEvent.keyCodeToString(mKeyCode) + ": executable");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e8b54d58..3000a1c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -103,6 +103,7 @@
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 @@
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 @@
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 @@
}
Looper getLooper() {
- return mLooper;
+ return Looper.myLooper();
}
AccessibilityShortcutController getAccessibilityShortcutController(
@@ -2195,7 +2198,7 @@
/** {@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 @@
@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 @@
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 @@
}
}
+ /**
+ * 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,15 +2792,49 @@
// 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;
+ }
}
private void initSingleKeyGestureRules(Looper looper) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
index d2ef180..ca3787e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
@@ -95,6 +95,26 @@
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;
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index f7ad2a8..50d37ec 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -19,14 +19,18 @@
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 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 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 @@
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 @@
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true);
+
setDispatchedKeyHandler(keyEvent -> true);
sendKey(KEYCODE_STEM_PRIMARY);
@@ -131,6 +139,7 @@
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 @@
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 @@
@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 @@
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();
@@ -189,9 +233,16 @@
sendKey(KEYCODE_STEM_PRIMARY);
sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertNotOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertNotOpenAllAppView();
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
@Test
@@ -215,7 +266,7 @@
sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertNotOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
private void overrideBehavior(String key, int expectedBehavior) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 0678210..7c2f7ee 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -73,6 +73,7 @@
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 @@
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 @@
@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 @@
mPhoneWindowManager.mShouldEarlyShortPressOnStemPrimary = shouldEarlyShortPress;
}
+ void overrideTalkbackShortcutGestureEnabled(boolean enabled) {
+ mIsTalkBackShortcutGestureEnabled = enabled;
+ }
+
// Override assist perform function.
void overrideLongPressOnPower(int behavior) {
mPhoneWindowManager.mLongPressOnPowerBehavior = behavior;
@@ -714,7 +725,7 @@
}
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 @@
}
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 @@
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);
+ }
}