summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author PETER LIANG <peterliang@google.com> 2022-11-18 04:54:28 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-11-18 04:54:28 +0000
commitc2a03fd6272c4b2f0df539f60775c0da771ecd0d (patch)
treef71138e09afb9ced066366be86787837be211e33
parent42051221a048bed64e5553188a0802bcbdd6a724 (diff)
parent8dca8a3434230643649718b82692253fd2cd9a39 (diff)
Merge changes from topic "ig"
* changes: Fix that accessibility floating menu did not remember tucked state when lock/unlock the phone. Refactor the design and improve the animations of Accessibility Floating Menu(9/n).
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/Prefs.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java109
11 files changed, 314 insertions, 44 deletions
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 437d89beaa9e..8501b5c4547b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1341,6 +1341,7 @@
<dimen name="accessibility_floating_menu_large_width_height">56dp</dimen>
<dimen name="accessibility_floating_menu_large_single_radius">35dp</dimen>
<dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
+ <dimen name="accessibility_floating_menu_ime_shifting_space">48dp</dimen>
<dimen name="accessibility_floating_menu_message_container_horizontal_padding">15dp</dimen>
<dimen name="accessibility_floating_menu_message_text_vertical_padding">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 9aa5fae1044d..70750a1dafb5 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -72,7 +72,8 @@ public final class Prefs {
Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP,
Key.ACCESSIBILITY_FLOATING_MENU_POSITION,
Key.HAS_CLICKED_NUDGE_TO_SETUP_DREAM,
- Key.HAS_DISMISSED_NUDGE_TO_SETUP_DREAM
+ Key.HAS_DISMISSED_NUDGE_TO_SETUP_DREAM,
+ Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED
})
// TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them
public @interface Key {
@@ -117,6 +118,7 @@ public final class Prefs {
String ACCESSIBILITY_FLOATING_MENU_POSITION = "AccessibilityFloatingMenuPosition";
String HAS_CLICKED_NUDGE_TO_SETUP_DREAM = "HasClickedNudgeToSetupDream";
String HAS_DISMISSED_NUDGE_TO_SETUP_DREAM = "HasDismissedNudgeToSetupDream";
+ String HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED = "HasAccessibilityFloatingMenuTucked";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 396f584d76a6..1e14763e57d5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -16,8 +16,6 @@
package com.android.systemui.accessibility.floatingmenu;
-import static android.util.MathUtils.constrain;
-
import static java.util.Objects.requireNonNull;
import android.animation.ValueAnimator;
@@ -64,7 +62,6 @@ class MenuAnimationController {
private final MenuView mMenuView;
private final ValueAnimator mFadeOutAnimator;
private final Handler mHandler;
- private boolean mIsMovedToEdge;
private boolean mIsFadeEffectEnabled;
private DismissAnimationController.DismissCallback mDismissCallback;
@@ -111,25 +108,25 @@ class MenuAnimationController {
}
void moveToTopLeftPosition() {
- mIsMovedToEdge = false;
+ mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
moveAndPersistPosition(new PointF(draggableBounds.left, draggableBounds.top));
}
void moveToTopRightPosition() {
- mIsMovedToEdge = false;
+ mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
moveAndPersistPosition(new PointF(draggableBounds.right, draggableBounds.top));
}
void moveToBottomLeftPosition() {
- mIsMovedToEdge = false;
+ mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
moveAndPersistPosition(new PointF(draggableBounds.left, draggableBounds.bottom));
}
void moveToBottomRightPosition() {
- mIsMovedToEdge = false;
+ mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
moveAndPersistPosition(new PointF(draggableBounds.right, draggableBounds.bottom));
}
@@ -254,6 +251,8 @@ class MenuAnimationController {
// If the translation x is zero, it should be at the left of the bound.
if (currentXTranslation < draggableBounds.left
|| currentXTranslation > draggableBounds.right) {
+ constrainPositionAndUpdate(
+ new PointF(mMenuView.getTranslationX(), mMenuView.getTranslationY()));
moveToEdgeAndHide();
return true;
}
@@ -262,37 +261,33 @@ class MenuAnimationController {
return false;
}
- private boolean isOnLeftSide() {
+ boolean isOnLeftSide() {
return mMenuView.getTranslationX() < mMenuView.getMenuDraggableBounds().centerX();
}
- boolean isMovedToEdge() {
- return mIsMovedToEdge;
+ boolean isMoveToTucked() {
+ return mMenuView.isMoveToTucked();
}
void moveToEdgeAndHide() {
- mIsMovedToEdge = true;
+ mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ true);
- final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
- final float endY = constrain(mMenuView.getTranslationY(), draggableBounds.top,
- draggableBounds.bottom);
- final float menuHalfWidth = mMenuView.getWidth() / 2.0f;
+ final PointF position = mMenuView.getMenuPosition();
+ final float menuHalfWidth = mMenuView.getMenuWidth() / 2.0f;
final float endX = isOnLeftSide()
- ? draggableBounds.left - menuHalfWidth
- : draggableBounds.right + menuHalfWidth;
- moveAndPersistPosition(new PointF(endX, endY));
+ ? position.x - menuHalfWidth
+ : position.x + menuHalfWidth;
+ moveToPosition(new PointF(endX, position.y));
// Keep the touch region let users could click extra space to pop up the menu view
// from the screen edge
- mMenuView.onBoundsInParentChanged(isOnLeftSide()
- ? draggableBounds.left
- : draggableBounds.right, (int) mMenuView.getTranslationY());
+ mMenuView.onBoundsInParentChanged((int) position.x, (int) position.y);
fadeOutIfEnabled();
}
void moveOutEdgeAndShow() {
- mIsMovedToEdge = false;
+ mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
mMenuView.onPositionChanged();
mMenuView.onEdgeChangedIfNeeded();
@@ -345,7 +340,7 @@ class MenuAnimationController {
}
private void constrainPositionAndUpdate(PointF position) {
- final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
+ final Rect draggableBounds = mMenuView.getMenuDraggableBoundsExcludeIme();
// Have the space gap margin between the top bound and the menu view, so actually the
// position y range needs to cut the margin.
position.offset(-draggableBounds.left, -draggableBounds.top);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index 4c52b331497d..5bc7406bb9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -51,6 +51,7 @@ class MenuInfoRepository {
@FloatRange(from = 0.0, to = 1.0)
private static final float DEFAULT_MENU_POSITION_Y_PERCENT = 0.77f;
+ private static final boolean DEFAULT_MOVE_TO_TUCKED_VALUE = false;
private final Context mContext;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -92,6 +93,12 @@ class MenuInfoRepository {
mPercentagePosition = getStartPosition();
}
+ void loadMenuMoveToTucked(OnInfoReady<Boolean> callback) {
+ callback.onReady(
+ Prefs.getBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
+ DEFAULT_MOVE_TO_TUCKED_VALUE));
+ }
+
void loadMenuPosition(OnInfoReady<Position> callback) {
callback.onReady(mPercentagePosition);
}
@@ -113,6 +120,11 @@ class MenuInfoRepository {
getMenuOpacityFromSettings(mContext));
}
+ void updateMoveToTucked(boolean isMoveToTucked) {
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
+ isMoveToTucked);
+ }
+
void updateMenuSavingPosition(Position percentagePosition) {
mPercentagePosition = percentagePosition;
Prefs.putString(mContext, Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
index ac5736b0c26d..14517ba5bdb4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegate.java
@@ -74,10 +74,10 @@ class MenuItemAccessibilityDelegate extends RecyclerViewAccessibilityDelegate.It
R.string.accessibility_floating_button_action_move_bottom_right));
info.addAction(moveBottomRight);
- final int moveEdgeId = mAnimationController.isMovedToEdge()
+ final int moveEdgeId = mAnimationController.isMoveToTucked()
? R.id.action_move_out_edge_and_show
: R.id.action_move_to_edge_and_hide;
- final int moveEdgeTextResId = mAnimationController.isMovedToEdge()
+ final int moveEdgeTextResId = mAnimationController.isMoveToTucked()
? R.string.accessibility_floating_button_action_move_out_edge_and_show
: R.string.accessibility_floating_button_action_move_to_edge_and_hide_to_half;
final AccessibilityNodeInfoCompat.AccessibilityActionCompat moveToOrOutEdge =
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 6a14af52fbaf..986aa51ecce1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -58,12 +58,15 @@ class MenuView extends FrameLayout implements
this::updateSystemGestureExcludeRects;
private final Observer<MenuFadeEffectInfo> mFadeEffectInfoObserver =
this::onMenuFadeEffectInfoChanged;
+ private final Observer<Boolean> mMoveToTuckedObserver = this::onMoveToTucked;
private final Observer<Position> mPercentagePositionObserver = this::onPercentagePosition;
private final Observer<Integer> mSizeTypeObserver = this::onSizeTypeChanged;
private final Observer<List<AccessibilityTarget>> mTargetFeaturesObserver =
this::onTargetFeaturesChanged;
private final MenuViewAppearance mMenuViewAppearance;
+ private boolean mIsMoveToTucked;
+
private OnTargetFeaturesChangeListener mFeaturesChangeListener;
MenuView(Context context, MenuViewModel menuViewModel, MenuViewAppearance menuViewAppearance) {
@@ -161,6 +164,12 @@ class MenuView extends FrameLayout implements
mMenuViewAppearance.getMenuStrokeColor());
}
+ private void onMoveToTucked(boolean isMoveToTucked) {
+ mIsMoveToTucked = isMoveToTucked;
+
+ onPositionChanged();
+ }
+
private void onPercentagePosition(Position percentagePosition) {
mMenuViewAppearance.setPercentagePosition(percentagePosition);
@@ -171,6 +180,10 @@ class MenuView extends FrameLayout implements
final PointF position = mMenuViewAppearance.getMenuPosition();
mMenuAnimationController.moveToPosition(position);
onBoundsInParentChanged((int) position.x, (int) position.y);
+
+ if (isMoveToTucked()) {
+ mMenuAnimationController.moveToEdgeAndHide();
+ }
}
@SuppressLint("NotifyDataSetChanged")
@@ -219,6 +232,22 @@ class MenuView extends FrameLayout implements
return mMenuViewAppearance.getMenuDraggableBounds();
}
+ Rect getMenuDraggableBoundsExcludeIme() {
+ return mMenuViewAppearance.getMenuDraggableBoundsExcludeIme();
+ }
+
+ int getMenuHeight() {
+ return mMenuViewAppearance.getMenuHeight();
+ }
+
+ int getMenuWidth() {
+ return mMenuViewAppearance.getMenuWidth();
+ }
+
+ PointF getMenuPosition() {
+ return mMenuViewAppearance.getMenuPosition();
+ }
+
void persistPositionAndUpdateEdge(Position percentagePosition) {
mMenuViewModel.updateMenuSavingPosition(percentagePosition);
mMenuViewAppearance.setPercentagePosition(percentagePosition);
@@ -226,6 +255,16 @@ class MenuView extends FrameLayout implements
onEdgeChangedIfNeeded();
}
+ boolean isMoveToTucked() {
+ return mIsMoveToTucked;
+ }
+
+ void updateMenuMoveToTucked(boolean isMoveToTucked) {
+ mIsMoveToTucked = isMoveToTucked;
+ mMenuViewModel.updateMenuMoveToTucked(isMoveToTucked);
+ }
+
+
/**
* Uses the touch events from the parent view to identify if users clicked the extra
* space of the menu view. If yes, will use the percentage position and update the
@@ -241,7 +280,7 @@ class MenuView extends FrameLayout implements
boolean maybeMoveOutEdgeAndShow(int x, int y) {
// Utilizes the touch region of the parent view to implement that users could tap extra
// the space region to show the menu from the edge.
- if (!mMenuAnimationController.isMovedToEdge() || !mBoundsInParent.contains(x, y)) {
+ if (!isMoveToTucked() || !mBoundsInParent.contains(x, y)) {
return false;
}
@@ -258,6 +297,7 @@ class MenuView extends FrameLayout implements
mMenuViewModel.getFadeEffectInfoData().observeForever(mFadeEffectInfoObserver);
mMenuViewModel.getTargetFeaturesData().observeForever(mTargetFeaturesObserver);
mMenuViewModel.getSizeTypeData().observeForever(mSizeTypeObserver);
+ mMenuViewModel.getMoveToTuckedData().observeForever(mMoveToTuckedObserver);
setVisibility(VISIBLE);
mMenuViewModel.registerContentObservers();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -271,6 +311,7 @@ class MenuView extends FrameLayout implements
mMenuViewModel.getFadeEffectInfoData().removeObserver(mFadeEffectInfoObserver);
mMenuViewModel.getTargetFeaturesData().removeObserver(mTargetFeaturesObserver);
mMenuViewModel.getSizeTypeData().removeObserver(mSizeTypeObserver);
+ mMenuViewModel.getMoveToTuckedData().removeObserver(mMoveToTuckedObserver);
mMenuViewModel.unregisterContentObservers();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index 4a9807febc7f..a7cdeab7c127 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -47,6 +47,9 @@ class MenuViewAppearance {
private final Resources mRes;
private final Position mPercentagePosition = new Position(/* percentageX= */
0f, /* percentageY= */ 0f);
+ private boolean mIsImeShowing;
+ // Avoid the menu view overlapping on the primary action button under the bottom as possible.
+ private int mImeShiftingSpace;
private int mTargetFeaturesSize;
private int mSizeType;
private int mMargin;
@@ -62,6 +65,7 @@ class MenuViewAppearance {
private int mStrokeColor;
private int mInset;
private int mElevation;
+ private float mImeTop;
private float[] mRadii;
private Drawable mBackgroundDrawable;
private String mContentDescription;
@@ -106,6 +110,8 @@ class MenuViewAppearance {
mStrokeColor = mRes.getColor(R.color.accessibility_floating_menu_stroke_dark);
mInset = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_stroke_inset);
mElevation = mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
+ mImeShiftingSpace = mRes.getDimensionPixelSize(
+ R.dimen.accessibility_floating_menu_ime_shifting_space);
final Drawable drawable =
mRes.getDrawable(R.drawable.accessibility_floating_menu_background);
mBackgroundDrawable = new InstantInsetLayerDrawable(new Drawable[]{drawable});
@@ -131,29 +137,56 @@ class MenuViewAppearance {
mRadii = createRadii(isMenuOnLeftSide(), getMenuRadius(mTargetFeaturesSize));
}
+ void onImeVisibilityChanged(boolean imeShowing, float imeTop) {
+ mIsImeShowing = imeShowing;
+ mImeTop = imeTop;
+ }
+
Rect getMenuDraggableBounds() {
+ return getMenuDraggableBoundsWith(/* includeIme= */ true);
+ }
+
+ Rect getMenuDraggableBoundsExcludeIme() {
+ return getMenuDraggableBoundsWith(/* includeIme= */ false);
+ }
+
+ private Rect getMenuDraggableBoundsWith(boolean includeIme) {
final int margin = getMenuMargin();
- final Rect draggableBounds = getWindowAvailableBounds();
+ final Rect draggableBounds = new Rect(getWindowAvailableBounds());
// Initializes start position for mapping the translation of the menu view.
draggableBounds.offsetTo(/* newLeft= */ 0, /* newTop= */ 0);
draggableBounds.top += margin;
draggableBounds.right -= getMenuWidth();
- draggableBounds.bottom -= Math.min(
- getWindowAvailableBounds().height() - draggableBounds.top,
- calculateActualMenuHeight() + margin);
+
+ if (includeIme && mIsImeShowing) {
+ final int imeHeight = (int) (draggableBounds.bottom - mImeTop);
+ draggableBounds.bottom -= (imeHeight + mImeShiftingSpace);
+ }
+ draggableBounds.bottom -= (calculateActualMenuHeight() + margin);
+ draggableBounds.bottom = Math.max(draggableBounds.top, draggableBounds.bottom);
+
return draggableBounds;
}
PointF getMenuPosition() {
- final Rect draggableBounds = getMenuDraggableBounds();
-
- return new PointF(
- draggableBounds.left
- + draggableBounds.width() * mPercentagePosition.getPercentageX(),
- draggableBounds.top
- + draggableBounds.height() * mPercentagePosition.getPercentageY());
+ final Rect draggableBounds = getMenuDraggableBoundsExcludeIme();
+ final float x = draggableBounds.left
+ + draggableBounds.width() * mPercentagePosition.getPercentageX();
+
+ float y = draggableBounds.top
+ + draggableBounds.height() * mPercentagePosition.getPercentageY();
+
+ // If the bottom of the menu view and overlap on the ime, its position y will be
+ // overridden with new y.
+ final float menuBottom = y + getMenuHeight() + mMargin;
+ if (mIsImeShowing && (menuBottom >= mImeTop)) {
+ y = Math.max(draggableBounds.top,
+ mImeTop - getMenuHeight() - mMargin - mImeShiftingSpace);
+ }
+
+ return new PointF(x, y);
}
String getContentDescription() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index b8f14aef648a..c42943cf56e2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -16,6 +16,10 @@
package com.android.systemui.accessibility.floatingmenu;
+import static android.view.WindowInsets.Type.ime;
+
+import static androidx.core.view.WindowInsetsCompat.Type;
+
import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType.INVISIBLE_TOGGLE;
import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
@@ -26,13 +30,16 @@ import android.annotation.IntDef;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.PluralsMessageFormatter;
import android.view.MotionEvent;
+import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -62,14 +69,17 @@ import java.util.Map;
class MenuViewLayer extends FrameLayout {
private static final int SHOW_MESSAGE_DELAY_MS = 3000;
+ private final WindowManager mWindowManager;
private final MenuView mMenuView;
private final MenuMessageView mMessageView;
private final DismissView mDismissView;
+ private final MenuViewAppearance mMenuViewAppearance;
private final MenuAnimationController mMenuAnimationController;
private final AccessibilityManager mAccessibilityManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final IAccessibilityFloatingMenu mFloatingMenu;
private final DismissAnimationController mDismissAnimationController;
+ private final Rect mImeInsetsRect = new Rect();
@IntDef({
LayerIndex.MENU_VIEW,
@@ -111,13 +121,13 @@ class MenuViewLayer extends FrameLayout {
AccessibilityManager accessibilityManager, IAccessibilityFloatingMenu floatingMenu) {
super(context);
+ mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
mFloatingMenu = floatingMenu;
final MenuViewModel menuViewModel = new MenuViewModel(context);
- final MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context,
- windowManager);
- mMenuView = new MenuView(context, menuViewModel, menuViewAppearance);
+ mMenuViewAppearance = new MenuViewAppearance(context, windowManager);
+ mMenuView = new MenuView(context, menuViewModel, mMenuViewAppearance);
mMenuAnimationController = mMenuView.getMenuAnimationController();
mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage);
@@ -200,6 +210,7 @@ class MenuViewLayer extends FrameLayout {
super.onAttachedToWindow();
mMenuView.show();
+ setOnApplyWindowInsetsListener((view, insets) -> onWindowInsetsApplied(insets));
mMessageView.setUndoListener(view -> undo());
mContext.registerComponentCallbacks(mDismissAnimationController);
}
@@ -209,10 +220,35 @@ class MenuViewLayer extends FrameLayout {
super.onDetachedFromWindow();
mMenuView.hide();
+ setOnApplyWindowInsetsListener(null);
mHandler.removeCallbacksAndMessages(/* token= */ null);
mContext.unregisterComponentCallbacks(mDismissAnimationController);
}
+ private WindowInsets onWindowInsetsApplied(WindowInsets insets) {
+ final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+ final WindowInsets windowInsets = windowMetrics.getWindowInsets();
+ final Rect imeInsetsRect = windowInsets.getInsets(ime()).toRect();
+ if (!imeInsetsRect.equals(mImeInsetsRect)) {
+ final Rect windowBounds = new Rect(windowMetrics.getBounds());
+ final Rect systemBarsAndDisplayCutoutInsetsRect =
+ windowInsets.getInsetsIgnoringVisibility(
+ Type.systemBars() | Type.displayCutout()).toRect();
+ final float imeTop =
+ windowBounds.height() - systemBarsAndDisplayCutoutInsetsRect.top
+ - imeInsetsRect.bottom;
+
+ mMenuViewAppearance.onImeVisibilityChanged(windowInsets.isVisible(ime()), imeTop);
+
+ mMenuView.onEdgeChanged();
+ mMenuView.onPositionChanged();
+
+ mImeInsetsRect.set(imeInsetsRect);
+ }
+
+ return insets;
+ }
+
private void hideMenuAndShowMessage() {
final int delayTime = mAccessibilityManager.getRecommendedTimeoutMillis(
SHOW_MESSAGE_DELAY_MS,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
index e8a2b6e8767b..bd417877c124 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -35,6 +35,7 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
private final MutableLiveData<Integer> mSizeTypeData = new MutableLiveData<>();
private final MutableLiveData<MenuFadeEffectInfo> mFadeEffectInfoData =
new MutableLiveData<>();
+ private final MutableLiveData<Boolean> mMoveToTuckedData = new MutableLiveData<>();
private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>();
private final MenuInfoRepository mInfoRepository;
@@ -57,10 +58,19 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
mFadeEffectInfoData.setValue(fadeEffectInfo);
}
+ void updateMenuMoveToTucked(boolean isMoveToTucked) {
+ mInfoRepository.updateMoveToTucked(isMoveToTucked);
+ }
+
void updateMenuSavingPosition(Position percentagePosition) {
mInfoRepository.updateMenuSavingPosition(percentagePosition);
}
+ LiveData<Boolean> getMoveToTuckedData() {
+ mInfoRepository.loadMenuMoveToTucked(mMoveToTuckedData::setValue);
+ return mMoveToTuckedData;
+ }
+
LiveData<Position> getPercentagePositionData() {
mInfoRepository.loadMenuPosition(mPercentagePositionData::setValue);
return mPercentagePositionData;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index d0bd4f7026eb..b2c52668e057 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -32,8 +32,10 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Prefs;
import com.android.systemui.SysuiTestCase;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,6 +46,7 @@ import org.junit.runner.RunWith;
@SmallTest
public class MenuAnimationControllerTest extends SysuiTestCase {
+ private boolean mLastIsMoveToTucked;
private ViewPropertyAnimator mViewPropertyAnimator;
private MenuView mMenuView;
private MenuAnimationController mMenuAnimationController;
@@ -60,6 +63,14 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
doReturn(mViewPropertyAnimator).when(mMenuView).animate();
mMenuAnimationController = new MenuAnimationController(mMenuView);
+ mLastIsMoveToTucked = Prefs.getBoolean(mContext,
+ Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED, /* defaultValue= */ false);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
+ mLastIsMoveToTucked);
}
@Test
@@ -81,10 +92,34 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
@Test
public void startGrowAnimation_menuCompletelyOpaque() {
- mMenuAnimationController.startShrinkAnimation(null);
+ mMenuAnimationController.startShrinkAnimation(/* endAction= */ null);
mMenuAnimationController.startGrowAnimation();
assertThat(mMenuView.getAlpha()).isEqualTo(/* completelyOpaque */ 1.0f);
}
+
+ @Test
+ public void moveToEdgeAndHide_untucked_expectedSharedPreferenceValue() {
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED, /* value= */
+ false);
+
+ mMenuAnimationController.moveToEdgeAndHide();
+ final boolean isMoveToTucked = Prefs.getBoolean(mContext,
+ Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED, /* defaultValue= */ false);
+
+ assertThat(isMoveToTucked).isTrue();
+ }
+
+ @Test
+ public void moveOutEdgeAndShow_tucked_expectedSharedPreferenceValue() {
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED, /* value= */
+ true);
+
+ mMenuAnimationController.moveOutEdgeAndShow();
+ final boolean isMoveToTucked = Prefs.getBoolean(mContext,
+ Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED, /* defaultValue= */ true);
+
+ assertThat(isMoveToTucked).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 2d5188fcb95a..428a00a6dcef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -18,12 +18,17 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.systemBars;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,13 +37,18 @@ import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.graphics.Insets;
+import android.graphics.PointF;
+import android.graphics.Rect;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
+import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
@@ -68,9 +78,20 @@ public class MenuViewLayerTest extends SysuiTestCase {
private static final ComponentName TEST_SELECT_TO_SPEAK_COMPONENT_NAME = new ComponentName(
SELECT_TO_SPEAK_PACKAGE_NAME, SELECT_TO_SPEAK_SERVICE_NAME);
+ private static final int DISPLAY_WINDOW_WIDTH = 1080;
+ private static final int DISPLAY_WINDOW_HEIGHT = 2340;
+ private static final int STATUS_BAR_HEIGHT = 75;
+ private static final int NAVIGATION_BAR_HEIGHT = 125;
+ private static final int IME_HEIGHT = 350;
+ private static final int IME_TOP =
+ DISPLAY_WINDOW_HEIGHT - STATUS_BAR_HEIGHT - NAVIGATION_BAR_HEIGHT - IME_HEIGHT;
+
private MenuViewLayer mMenuViewLayer;
private String mLastAccessibilityButtonTargets;
private String mLastEnabledAccessibilityServices;
+ private WindowMetrics mWindowMetrics;
+ private MenuView mMenuView;
+ private MenuAnimationController mMenuAnimationController;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -79,13 +100,23 @@ public class MenuViewLayerTest extends SysuiTestCase {
private IAccessibilityFloatingMenu mFloatingMenu;
@Mock
+ private WindowManager mStubWindowManager;
+
+ @Mock
private AccessibilityManager mStubAccessibilityManager;
@Before
public void setUp() throws Exception {
- final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
- mMenuViewLayer = new MenuViewLayer(mContext, stubWindowManager, mStubAccessibilityManager,
+ final Rect mDisplayBounds = new Rect();
+ mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH,
+ DISPLAY_WINDOW_HEIGHT);
+ mWindowMetrics = spy(new WindowMetrics(mDisplayBounds, fakeDisplayInsets()));
+ doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
+
+ mMenuViewLayer = new MenuViewLayer(mContext, mStubWindowManager, mStubAccessibilityManager,
mFloatingMenu);
+ mMenuView = (MenuView) mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
+ mMenuAnimationController = mMenuView.getMenuAnimationController();
mLastAccessibilityButtonTargets =
Settings.Secure.getStringForUser(mContext.getContentResolver(),
@@ -93,6 +124,12 @@ public class MenuViewLayerTest extends SysuiTestCase {
mLastEnabledAccessibilityServices =
Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, UserHandle.USER_CURRENT);
+
+ mMenuViewLayer.onAttachedToWindow();
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "", UserHandle.USER_CURRENT);
}
@After
@@ -103,6 +140,9 @@ public class MenuViewLayerTest extends SysuiTestCase {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, mLastEnabledAccessibilityServices,
UserHandle.USER_CURRENT);
+
+ mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
+ mMenuViewLayer.onDetachedFromWindow();
}
@Test
@@ -168,4 +208,69 @@ public class MenuViewLayerTest extends SysuiTestCase {
assertThat(value).isEqualTo("");
}
+
+ @Test
+ public void showingImeInsetsChange_notOverlapOnIme_menuKeepOriginalPosition() {
+ final float menuTop = STATUS_BAR_HEIGHT + 100;
+ mMenuAnimationController.moveAndPersistPosition(new PointF(0, menuTop));
+
+ dispatchShowingImeInsets();
+
+ assertThat(mMenuView.getTranslationX()).isEqualTo(0);
+ assertThat(mMenuView.getTranslationY()).isEqualTo(menuTop);
+ }
+
+ @Test
+ public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme() {
+ final float menuTop = IME_TOP + 100;
+ mMenuAnimationController.moveAndPersistPosition(new PointF(0, menuTop));
+
+ dispatchShowingImeInsets();
+
+ final float menuBottom = mMenuView.getTranslationY() + mMenuView.getMenuHeight();
+ assertThat(mMenuView.getTranslationX()).isEqualTo(0);
+ assertThat(menuBottom).isLessThan(IME_TOP);
+ }
+
+ @Test
+ public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition() {
+ final float menuTop = IME_TOP + 200;
+ mMenuAnimationController.moveAndPersistPosition(new PointF(0, menuTop));
+ dispatchShowingImeInsets();
+
+ dispatchHidingImeInsets();
+
+ assertThat(mMenuView.getTranslationX()).isEqualTo(0);
+ assertThat(mMenuView.getTranslationY()).isEqualTo(menuTop);
+ }
+
+ private void dispatchShowingImeInsets() {
+ final WindowInsets fakeShowingImeInsets = fakeImeInsets(/* isImeVisible= */ true);
+ doReturn(fakeShowingImeInsets).when(mWindowMetrics).getWindowInsets();
+ mMenuViewLayer.dispatchApplyWindowInsets(fakeShowingImeInsets);
+ }
+
+ private void dispatchHidingImeInsets() {
+ final WindowInsets fakeHidingImeInsets = fakeImeInsets(/* isImeVisible= */ false);
+ doReturn(fakeHidingImeInsets).when(mWindowMetrics).getWindowInsets();
+ mMenuViewLayer.dispatchApplyWindowInsets(fakeHidingImeInsets);
+ }
+
+ private WindowInsets fakeDisplayInsets() {
+ return new WindowInsets.Builder()
+ .setVisible(systemBars() | displayCutout(), /* visible= */ true)
+ .setInsets(systemBars() | displayCutout(),
+ Insets.of(/* left= */ 0, STATUS_BAR_HEIGHT, /* right= */ 0,
+ NAVIGATION_BAR_HEIGHT))
+ .build();
+ }
+
+ private WindowInsets fakeImeInsets(boolean isImeVisible) {
+ final int bottom = isImeVisible ? (IME_HEIGHT + NAVIGATION_BAR_HEIGHT) : 0;
+ return new WindowInsets.Builder()
+ .setVisible(ime(), isImeVisible)
+ .setInsets(ime(),
+ Insets.of(/* left= */ 0, /* top= */ 0, /* right= */ 0, bottom))
+ .build();
+ }
}