diff options
| author | 2022-11-18 04:54:28 +0000 | |
|---|---|---|
| committer | 2022-11-18 04:54:28 +0000 | |
| commit | c2a03fd6272c4b2f0df539f60775c0da771ecd0d (patch) | |
| tree | f71138e09afb9ced066366be86787837be211e33 | |
| parent | 42051221a048bed64e5553188a0802bcbdd6a724 (diff) | |
| parent | 8dca8a3434230643649718b82692253fd2cd9a39 (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).
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(); + } } |