Fixing the a11y floating menu could overlap the taskbar in a foldable device.

Actions:
1. Replacing the DisplayMetrics with WindowMettrics.
2. Using the window inset of the system bar.
3. Rename the "Screen" words to "Display".

Bug: 194073213
Bug: 194071997
Bug: 189193252
Test: atest AccessibilityFloatingMenuViewTest -c
Change-Id: Ic5d88cf3a51b1a230397735b0dda352278c539c4
This commit is contained in:
Peter_Liang
2021-09-09 00:31:00 +08:00
parent 6fbb385f24
commit 0086704424
5 changed files with 111 additions and 41 deletions

View File

@@ -19,8 +19,10 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.util.MathUtils.constrain;
import static android.util.MathUtils.sq;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.systemBars;
import static java.util.Objects.requireNonNull;
@@ -41,7 +43,6 @@ import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -108,8 +109,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout
private int mRadiusType;
private int mMargin;
private int mPadding;
private int mScreenHeight;
private int mScreenWidth;
// The display width excludes the window insets of the system bar and display cutout.
private int mDisplayHeight;
// The display Height excludes the window insets of the system bar and display cutout.
private int mDisplayWidth;
private int mIconWidth;
private int mIconHeight;
private int mInset;
@@ -118,6 +121,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
private int mRelativeToPointerDownX;
private int mRelativeToPointerDownY;
private float mRadius;
private final Rect mDisplayInsetsRect = new Rect();
private final Position mPosition;
private float mSquareScaledTouchSlop;
private final Configuration mLastConfiguration;
@@ -506,6 +510,13 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
private WindowInsets onWindowInsetsApplied(WindowInsets insets) {
final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
final Rect displayWindowInsetsRect = getDisplayInsets(windowMetrics).toRect();
if (!displayWindowInsetsRect.equals(mDisplayInsetsRect)) {
updateDisplaySizeWith(windowMetrics);
updateLocationWith(mPosition);
}
final boolean currentImeVisibility = insets.isVisible(ime());
if (currentImeVisibility != mImeVisibility) {
mImeVisibility = currentImeVisibility;
@@ -546,9 +557,9 @@ public class AccessibilityFloatingMenuView extends FrameLayout
private void updateDimensions() {
final Resources res = getResources();
final DisplayMetrics dm = res.getDisplayMetrics();
mScreenWidth = dm.widthPixels;
mScreenHeight = dm.heightPixels;
updateDisplaySizeWith(mWindowManager.getCurrentWindowMetrics());
mMargin =
res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_margin);
mInset =
@@ -560,6 +571,15 @@ public class AccessibilityFloatingMenuView extends FrameLayout
updateItemViewDimensionsWith(mSizeType);
}
private void updateDisplaySizeWith(WindowMetrics metrics) {
final Rect displayBounds = metrics.getBounds();
final Insets displayInsets = getDisplayInsets(metrics);
mDisplayInsetsRect.set(displayInsets.toRect());
displayBounds.inset(displayInsets);
mDisplayWidth = displayBounds.width();
mDisplayHeight = displayBounds.height();
}
private void updateItemViewDimensionsWith(@SizeType int sizeType) {
final Resources res = getResources();
final int paddingResId =
@@ -684,11 +704,11 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
private int getMaxWindowX() {
return mScreenWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth();
return mDisplayWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth();
}
private int getMaxWindowY() {
return mScreenHeight - getWindowHeight();
return mDisplayHeight - getWindowHeight();
}
private InstantInsetLayerDrawable getMenuLayerDrawable() {
@@ -699,8 +719,13 @@ public class AccessibilityFloatingMenuView extends FrameLayout
return (GradientDrawable) getMenuLayerDrawable().getDrawable(INDEX_MENU_ITEM);
}
private Insets getDisplayInsets(WindowMetrics metrics) {
return metrics.getWindowInsets().getInsetsIgnoringVisibility(
systemBars() | displayCutout());
}
/**
* Updates the floating menu to be fixed at the side of the screen.
* Updates the floating menu to be fixed at the side of the display.
*/
private void updateLocationWith(Position position) {
final @Alignment int alignment = transformToAlignment(position.getPercentageX());
@@ -723,7 +748,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
final Insets imeInsets = windowMetrics.getWindowInsets().getInsets(
ime() | navigationBars());
final int imeY = mScreenHeight - imeInsets.bottom;
final int imeY = mDisplayHeight - imeInsets.bottom;
final int layoutBottomY = mCurrentLayoutParams.y + getWindowHeight();
return layoutBottomY > imeY ? (layoutBottomY - imeY) : 0;
@@ -855,11 +880,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout
@VisibleForTesting
Rect getAvailableBounds() {
return new Rect(0, 0, mScreenWidth - getWindowWidth(), mScreenHeight - getWindowHeight());
return new Rect(0, 0, mDisplayWidth - getWindowWidth(),
mDisplayHeight - getWindowHeight());
}
private int getMaxLayoutHeight() {
return mScreenHeight - mMargin * 2;
return mDisplayHeight - mMargin * 2;
}
private int getLayoutWidth() {
@@ -875,7 +901,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
private int getWindowHeight() {
return Math.min(mScreenHeight, mMargin * 2 + getLayoutHeight());
return Math.min(mDisplayHeight, mMargin * 2 + getLayoutHeight());
}
private void setSystemGestureExclusion() {

View File

@@ -19,8 +19,10 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.View.OVER_SCROLL_ALWAYS;
import static android.view.View.OVER_SCROLL_NEVER;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.systemBars;
import static com.google.common.truth.Truth.assertThat;
@@ -39,6 +41,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.testing.AndroidTestingRunner;
@@ -98,14 +101,15 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
private AccessibilityFloatingMenuView mMenuView;
private RecyclerView mListView = new RecyclerView(mContext);
private int mScreenHeight;
private int mMenuWindowHeight;
private int mMenuHalfWidth;
private int mMenuHalfHeight;
private int mScreenHalfWidth;
private int mScreenHalfHeight;
private int mDisplayHalfWidth;
private int mDisplayHalfHeight;
private int mMaxWindowX;
private int mMaxWindowY;
private final int mDisplayWindowWidth = 1080;
private final int mDisplayWindowHeight = 2340;
@Before
public void initMenuView() {
@@ -113,7 +117,10 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, mDisplayWindowWidth,
mDisplayWindowHeight));
when(mWindowMetrics.getWindowInsets()).thenReturn(fakeDisplayInsets());
mMenuView = spy(
new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition, mListView));
}
@@ -129,18 +136,16 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_width_height);
final int menuWidth = padding * 2 + iconWidthHeight;
final int menuHeight = (padding + iconWidthHeight) * mTargets.size() + padding;
final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
mScreenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
mMenuHalfWidth = menuWidth / 2;
mMenuHalfHeight = menuHeight / 2;
mScreenHalfWidth = screenWidth / 2;
mScreenHalfHeight = mScreenHeight / 2;
mDisplayHalfWidth = mDisplayWindowWidth / 2;
mDisplayHalfHeight = mDisplayWindowHeight / 2;
int marginStartEnd =
mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT
? margin : 0;
mMaxWindowX = screenWidth - marginStartEnd - menuWidth;
mMaxWindowX = mDisplayWindowWidth - marginStartEnd - menuWidth;
mMenuWindowHeight = menuHeight + margin * 2;
mMaxWindowY = mScreenHeight - mMenuWindowHeight;
mMaxWindowY = mDisplayWindowHeight - mMenuWindowHeight;
}
@Test
@@ -279,15 +284,15 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
final MotionEvent moveEvent =
mMotionEventHelper.obtainMotionEvent(2, 3,
MotionEvent.ACTION_MOVE,
/* screenCenterX */mScreenHalfWidth
- /* offsetXToScreenLeftHalfRegion */ 10,
/* screenCenterY */ mScreenHalfHeight);
/* displayCenterX */mDisplayHalfWidth
- /* offsetXToDisplayLeftHalfRegion */ 10,
/* displayCenterY */ mDisplayHalfHeight);
final MotionEvent upEvent =
mMotionEventHelper.obtainMotionEvent(4, 5,
MotionEvent.ACTION_UP,
/* screenCenterX */ mScreenHalfWidth
- /* offsetXToScreenLeftHalfRegion */ 10,
/* screenCenterY */ mScreenHalfHeight);
/* displayCenterX */ mDisplayHalfWidth
- /* offsetXToDisplayLeftHalfRegion */ 10,
/* displayCenterY */ mDisplayHalfHeight);
listView.dispatchTouchEvent(downEvent);
listView.dispatchTouchEvent(moveEvent);
@@ -315,15 +320,15 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
final MotionEvent moveEvent =
mMotionEventHelper.obtainMotionEvent(2, 3,
MotionEvent.ACTION_MOVE,
/* screenCenterX */mScreenHalfWidth
+ /* offsetXToScreenRightHalfRegion */ 10,
/* screenCenterY */ mScreenHalfHeight);
/* displayCenterX */mDisplayHalfWidth
+ /* offsetXToDisplayRightHalfRegion */ 10,
/* displayCenterY */ mDisplayHalfHeight);
final MotionEvent upEvent =
mMotionEventHelper.obtainMotionEvent(4, 5,
MotionEvent.ACTION_UP,
/* screenCenterX */ mScreenHalfWidth
+ /* offsetXToScreenRightHalfRegion */ 10,
/* screenCenterY */ mScreenHalfHeight);
/* displayCenterX */ mDisplayHalfWidth
+ /* offsetXToDisplayRightHalfRegion */ 10,
/* displayCenterY */ mDisplayHalfHeight);
listView.dispatchTouchEvent(downEvent);
listView.dispatchTouchEvent(moveEvent);
@@ -332,12 +337,12 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
assertThat((float) menuView.mCurrentLayoutParams.x).isWithin(1.0f).of(mMaxWindowX);
assertThat((float) menuView.mCurrentLayoutParams.y).isWithin(1.0f).of(
/* newWindowY = screenCenterY - offsetY */ mScreenHalfHeight - mMenuHalfHeight);
/* newWindowY = displayCenterY - offsetY */ mDisplayHalfHeight - mMenuHalfHeight);
}
@Test
public void tapOnAndDragMenuToScreenSide_transformShapeHalfOval() {
public void tapOnAndDragMenuToDisplaySide_transformShapeHalfOval() {
final Position alignRightPosition = new Position(1.0f, 0.8f);
final RecyclerView listView = new RecyclerView(mContext);
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
@@ -355,13 +360,13 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
mMotionEventHelper.obtainMotionEvent(2, 3,
MotionEvent.ACTION_MOVE,
/* downX */(currentWindowX + mMenuHalfWidth)
+ /* offsetXToScreenRightSide */ mMenuHalfWidth,
+ /* offsetXToDisplayRightSide */ mMenuHalfWidth,
/* downY */ (currentWindowY + mMenuHalfHeight));
final MotionEvent upEvent =
mMotionEventHelper.obtainMotionEvent(4, 5,
MotionEvent.ACTION_UP,
/* downX */(currentWindowX + mMenuHalfWidth)
+ /* offsetXToScreenRightSide */ mMenuHalfWidth,
+ /* offsetXToDisplayRightSide */ mMenuHalfWidth,
/* downY */ (currentWindowY + mMenuHalfHeight));
listView.dispatchTouchEvent(downEvent);
@@ -423,7 +428,7 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
}
@Test
public void showMenuAndIme_withHigherIme_alignScreenTopEdge() {
public void showMenuAndIme_withHigherIme_alignDisplayTopEdge() {
final int offset = 99999;
setupBasicMenuView(mMenuView);
@@ -475,13 +480,24 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
private WindowInsets fakeImeInsetWith(AccessibilityFloatingMenuView menuView, int offset) {
// Ensure the keyboard has overlapped on the menu view.
final int fakeImeHeight =
mScreenHeight - (menuView.mCurrentLayoutParams.y + mMenuWindowHeight) + offset;
mDisplayWindowHeight - (menuView.mCurrentLayoutParams.y + mMenuWindowHeight)
+ offset;
return new WindowInsets.Builder()
.setVisible(ime() | navigationBars(), true)
.setInsets(ime() | navigationBars(), Insets.of(0, 0, 0, fakeImeHeight))
.build();
}
private WindowInsets fakeDisplayInsets() {
final int fakeStatusBarHeight = 75;
final int fakeNavigationBarHeight = 125;
return new WindowInsets.Builder()
.setVisible(systemBars() | displayCutout(), true)
.setInsets(systemBars() | displayCutout(),
Insets.of(0, fakeStatusBarHeight, 0, fakeNavigationBarHeight))
.build();
}
private class TestAccessibilityFloatingMenu extends AccessibilityFloatingMenuView {
TestAccessibilityFloatingMenu(Context context, Position position, RecyclerView listView) {
super(context, position, listView);

View File

@@ -23,12 +23,16 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.filters.SmallTest;
@@ -52,6 +56,9 @@ public class BaseTooltipViewTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
private WindowMetrics mWindowMetrics;
private AccessibilityFloatingMenuView mMenuView;
private BaseTooltipView mToolTipView;
@@ -66,6 +73,9 @@ public class BaseTooltipViewTest extends SysuiTestCase {
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
when(mWindowMetrics.getBounds()).thenReturn(new Rect());
when(mWindowMetrics.getWindowInsets()).thenReturn(new WindowInsets.Builder().build());
mMenuView = new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition);
mToolTipView = new BaseTooltipView(mContext, mMenuView);

View File

@@ -21,12 +21,16 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.test.filters.SmallTest;
@@ -49,6 +53,9 @@ public class DockTooltipViewTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
private WindowMetrics mWindowMetrics;
private AccessibilityFloatingMenuView mMenuView;
private DockTooltipView mDockTooltipView;
private final Position mPlaceholderPosition = new Position(0.0f, 0.0f);
@@ -62,6 +69,9 @@ public class DockTooltipViewTest extends SysuiTestCase {
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
when(mWindowMetrics.getBounds()).thenReturn(new Rect());
when(mWindowMetrics.getWindowInsets()).thenReturn(new WindowInsets.Builder().build());
mMenuView = spy(new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition));
mDockTooltipView = new DockTooltipView(mContext, mMenuView);

View File

@@ -22,12 +22,15 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -57,6 +60,8 @@ public class ItemDelegateCompatTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
private WindowMetrics mWindowMetrics;
private RecyclerView mListView;
private AccessibilityFloatingMenuView mMenuView;
private ItemDelegateCompat mItemDelegateCompat;
@@ -69,6 +74,9 @@ public class ItemDelegateCompatTest extends SysuiTestCase {
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
when(mWindowMetrics.getBounds()).thenReturn(new Rect());
when(mWindowMetrics.getWindowInsets()).thenReturn(new WindowInsets.Builder().build());
mListView = new RecyclerView(mContext);
mMenuView =