From 32b05d40df6001170a7b2926640c9ce840676878 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Thu, 24 Jun 2021 22:17:43 +0800 Subject: [PATCH] Fixing the announcement of menu of a11y actions when using Talkback or Switch Access. Root cause: The accessibility delegate was used for the root view instead of items. Actions: 1) Add the accessibility delegate to the individual items of the RecyclerView, and then override the functions onInitializeAccessibilityNodeInfo() and performAccessibilityAction(). 2) Remove the overridden functions onInitializeAccessibilityNodeInfo() and performAccessibilityAction() from the root view. Bug: 190326499 Bug: 190138534 Test: manual test Change-Id: Ife1787c2d7bb3090e4d55aed76c303e9842a1a98 --- .../AccessibilityFloatingMenuView.java | 99 ++-------- .../floatingmenu/ItemDelegateCompat.java | 141 +++++++++++++++ .../AccessibilityFloatingMenuViewTest.java | 88 +-------- .../floatingmenu/ItemDelegateCompatTest.java | 170 ++++++++++++++++++ 4 files changed, 323 insertions(+), 175 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompat.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java index e891e5b64b3d0..17818cd9c7ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java @@ -39,7 +39,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.DisplayMetrics; @@ -50,8 +49,6 @@ import android.view.ViewGroup; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.Animation; import android.view.animation.OvershootInterpolator; import android.view.animation.TranslateAnimation; @@ -59,8 +56,10 @@ import android.widget.FrameLayout; import androidx.annotation.DimenRes; import androidx.annotation.NonNull; +import androidx.core.view.AccessibilityDelegateCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.internal.annotations.VisibleForTesting; @@ -332,54 +331,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout // Do Nothing } - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - setupAccessibilityActions(info); - } - - @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - fadeIn(); - - final Rect bounds = getAvailableBounds(); - if (action == R.id.action_move_top_left) { - setShapeType(ShapeType.OVAL); - snapToLocation(bounds.left, bounds.top); - return true; - } - - if (action == R.id.action_move_top_right) { - setShapeType(ShapeType.OVAL); - snapToLocation(bounds.right, bounds.top); - return true; - } - - if (action == R.id.action_move_bottom_left) { - setShapeType(ShapeType.OVAL); - snapToLocation(bounds.left, bounds.bottom); - return true; - } - - if (action == R.id.action_move_bottom_right) { - setShapeType(ShapeType.OVAL); - snapToLocation(bounds.right, bounds.bottom); - return true; - } - - if (action == R.id.action_move_to_edge_and_hide) { - setShapeType(ShapeType.HALF_OVAL); - return true; - } - - if (action == R.id.action_move_out_edge_and_show) { - setShapeType(ShapeType.OVAL); - return true; - } - - return super.performAccessibilityAction(action, arguments); - } - void show() { if (isShowing()) { return; @@ -526,43 +477,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout mUiHandler.postDelayed(() -> mFadeOutAnimator.start(), FADE_EFFECT_DURATION_MS); } - private void setupAccessibilityActions(AccessibilityNodeInfo info) { - final Resources res = mContext.getResources(); - final AccessibilityAction moveTopLeft = - new AccessibilityAction(R.id.action_move_top_left, - res.getString( - R.string.accessibility_floating_button_action_move_top_left)); - info.addAction(moveTopLeft); - - final AccessibilityAction moveTopRight = - new AccessibilityAction(R.id.action_move_top_right, - res.getString( - R.string.accessibility_floating_button_action_move_top_right)); - info.addAction(moveTopRight); - - final AccessibilityAction moveBottomLeft = - new AccessibilityAction(R.id.action_move_bottom_left, - res.getString( - R.string.accessibility_floating_button_action_move_bottom_left)); - info.addAction(moveBottomLeft); - - final AccessibilityAction moveBottomRight = - new AccessibilityAction(R.id.action_move_bottom_right, - res.getString( - R.string.accessibility_floating_button_action_move_bottom_right)); - info.addAction(moveBottomRight); - - final int moveEdgeId = mShapeType == ShapeType.OVAL - ? R.id.action_move_to_edge_and_hide - : R.id.action_move_out_edge_and_show; - final int moveEdgeTextResId = mShapeType == ShapeType.OVAL - ? R.string.accessibility_floating_button_action_move_to_edge_and_hide_to_half - : R.string.accessibility_floating_button_action_move_out_edge_and_show; - final AccessibilityAction moveToOrOutEdge = - new AccessibilityAction(moveEdgeId, res.getString(moveEdgeTextResId)); - info.addAction(moveToOrOutEdge); - } - private boolean onTouched(MotionEvent event) { final int action = event.getAction(); final int currentX = (int) event.getX(); @@ -685,6 +599,15 @@ public class AccessibilityFloatingMenuView extends FrameLayout mListView.setLayoutManager(layoutManager); mListView.addOnItemTouchListener(this); mListView.animate().setInterpolator(new OvershootInterpolator()); + mListView.setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(mListView) { + @NonNull + @Override + public AccessibilityDelegateCompat getItemDelegate() { + return new ItemDelegateCompat(this, + AccessibilityFloatingMenuView.this); + } + }); + updateListViewWith(mLastConfiguration); addView(mListView); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompat.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompat.java new file mode 100644 index 0000000000000..93b0676b49300 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompat.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.floatingmenu; + +import android.content.res.Resources; +import android.graphics.Rect; +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; + +import com.android.systemui.R; +import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType; + +import java.lang.ref.WeakReference; + +/** + * An accessibility item delegate for the individual items of the list view + * {@link AccessibilityFloatingMenuView}. + */ +final class ItemDelegateCompat extends RecyclerViewAccessibilityDelegate.ItemDelegate { + private final WeakReference mMenuViewRef; + + ItemDelegateCompat(@NonNull RecyclerViewAccessibilityDelegate recyclerViewDelegate, + AccessibilityFloatingMenuView menuView) { + super(recyclerViewDelegate); + this.mMenuViewRef = new WeakReference<>(menuView); + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + + if (mMenuViewRef.get() == null) { + return; + } + final AccessibilityFloatingMenuView menuView = mMenuViewRef.get(); + + final Resources res = menuView.getResources(); + final AccessibilityNodeInfoCompat.AccessibilityActionCompat moveTopLeft = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat(R.id.action_move_top_left, + res.getString( + R.string.accessibility_floating_button_action_move_top_left)); + info.addAction(moveTopLeft); + + final AccessibilityNodeInfoCompat.AccessibilityActionCompat moveTopRight = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat( + R.id.action_move_top_right, + res.getString( + R.string.accessibility_floating_button_action_move_top_right)); + info.addAction(moveTopRight); + + final AccessibilityNodeInfoCompat.AccessibilityActionCompat moveBottomLeft = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat( + R.id.action_move_bottom_left, + res.getString( + R.string.accessibility_floating_button_action_move_bottom_left)); + info.addAction(moveBottomLeft); + + final AccessibilityNodeInfoCompat.AccessibilityActionCompat moveBottomRight = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat( + R.id.action_move_bottom_right, + res.getString( + R.string.accessibility_floating_button_action_move_bottom_right)); + info.addAction(moveBottomRight); + + final int moveEdgeId = menuView.isOvalShape() + ? R.id.action_move_to_edge_and_hide + : R.id.action_move_out_edge_and_show; + final int moveEdgeTextResId = menuView.isOvalShape() + ? R.string.accessibility_floating_button_action_move_to_edge_and_hide_to_half + : R.string.accessibility_floating_button_action_move_out_edge_and_show; + final AccessibilityNodeInfoCompat.AccessibilityActionCompat moveToOrOutEdge = + new AccessibilityNodeInfoCompat.AccessibilityActionCompat(moveEdgeId, + res.getString(moveEdgeTextResId)); + info.addAction(moveToOrOutEdge); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (mMenuViewRef.get() == null) { + return super.performAccessibilityAction(host, action, args); + } + final AccessibilityFloatingMenuView menuView = mMenuViewRef.get(); + + menuView.fadeIn(); + + final Rect bounds = menuView.getAvailableBounds(); + if (action == R.id.action_move_top_left) { + menuView.setShapeType(ShapeType.OVAL); + menuView.snapToLocation(bounds.left, bounds.top); + return true; + } + + if (action == R.id.action_move_top_right) { + menuView.setShapeType(ShapeType.OVAL); + menuView.snapToLocation(bounds.right, bounds.top); + return true; + } + + if (action == R.id.action_move_bottom_left) { + menuView.setShapeType(ShapeType.OVAL); + menuView.snapToLocation(bounds.left, bounds.bottom); + return true; + } + + if (action == R.id.action_move_bottom_right) { + menuView.setShapeType(ShapeType.OVAL); + menuView.snapToLocation(bounds.right, bounds.bottom); + return true; + } + + if (action == R.id.action_move_to_edge_and_hide) { + menuView.setShapeType(ShapeType.HALF_OVAL); + return true; + } + + if (action == R.id.action_move_out_edge_and_show) { + menuView.setShapeType(ShapeType.OVAL); + return true; + } + + return super.performAccessibilityAction(host, action, args); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java index 09e6940acd432..06e27b5dbf486 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java @@ -38,7 +38,6 @@ 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; @@ -49,7 +48,6 @@ import android.view.ViewPropertyAnimator; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; -import android.view.accessibility.AccessibilityNodeInfo; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; @@ -87,7 +85,6 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase { private final List mTargets = new ArrayList<>( Collections.singletonList(mock(AccessibilityTarget.class))); - private final Rect mAvailableBounds = new Rect(100, 200, 300, 400); private final Position mPlaceholderPosition = new Position(0.0f, 0.0f); @Mock @@ -144,6 +141,7 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase { @Test public void initListView_success() { + assertThat(mListView.getCompatAccessibilityDelegate()).isNotNull(); assertThat(mMenuView.getChildCount()).isEqualTo(1); } @@ -369,90 +367,6 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase { assertThat(menuView.mShapeType).isEqualTo(/* halfOval */ 1); } - @Test - public void getAccessibilityActionList_matchResult() { - final AccessibilityNodeInfo info = new AccessibilityNodeInfo(); - - mMenuView.onInitializeAccessibilityNodeInfo(info); - - assertThat(info.getActionList().size()).isEqualTo(5); - } - - @Test - public void accessibilityActionMove_halfOval_moveTopLeft_success() { - doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); - mMenuView.setShapeType(/* halfOvalShape */ 1); - - final boolean moveTopLeftAction = - mMenuView.performAccessibilityAction(R.id.action_move_top_left, null); - - assertThat(moveTopLeftAction).isTrue(); - assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); - verify(mMenuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.top); - } - - @Test - public void accessibilityActionMove_halfOval_moveTopRight_success() { - doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); - mMenuView.setShapeType(/* halfOvalShape */ 1); - - final boolean moveTopRightAction = - mMenuView.performAccessibilityAction(R.id.action_move_top_right, null); - - assertThat(moveTopRightAction).isTrue(); - assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); - verify(mMenuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.top); - } - - @Test - public void accessibilityActionMove_halfOval_moveBottomLeft_success() { - doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); - mMenuView.setShapeType(/* halfOvalShape */ 1); - - final boolean moveBottomLeftAction = - mMenuView.performAccessibilityAction(R.id.action_move_bottom_left, null); - - assertThat(moveBottomLeftAction).isTrue(); - assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); - verify(mMenuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.bottom); - } - - @Test - public void accessibilityActionMove_halfOval_moveBottomRight_success() { - doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); - mMenuView.setShapeType(/* halfOvalShape */ 1); - - final boolean moveBottomRightAction = - mMenuView.performAccessibilityAction(R.id.action_move_bottom_right, null); - - assertThat(moveBottomRightAction).isTrue(); - assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); - verify(mMenuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.bottom); - } - - @Test - public void accessibilityActionMove_halfOval_moveOutEdgeAndShow_success() { - doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); - mMenuView.setShapeType(/* halfOvalShape */ 1); - - final boolean moveOutEdgeAndShowAction = - mMenuView.performAccessibilityAction(R.id.action_move_out_edge_and_show, null); - - assertThat(moveOutEdgeAndShowAction).isTrue(); - assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); - } - - @Test - public void setupAccessibilityActions_oval_hasActionMoveToEdgeAndHide() { - final AccessibilityNodeInfo info = new AccessibilityNodeInfo(); - mMenuView.setShapeType(/* ovalShape */ 0); - - mMenuView.onInitializeAccessibilityNodeInfo(info); - - assertThat(info.getActionList().stream().anyMatch( - action -> action.getId() == R.id.action_move_to_edge_and_hide)).isTrue(); - } - @Test public void onTargetsChanged_exceedAvailableHeight_overScrollAlways() { doReturn(true).when(mMenuView).hasExceededMaxLayoutHeight(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java new file mode 100644 index 0000000000000..dae436427319e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.floatingmenu; + +import static com.google.common.truth.Truth.assertThat; + +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 android.content.Context; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityNodeInfo; + +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** Tests for {@link ItemDelegateCompat}. */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class ItemDelegateCompatTest extends SysuiTestCase { + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private WindowManager mWindowManager; + + private RecyclerView mListView; + private AccessibilityFloatingMenuView mMenuView; + private ItemDelegateCompat mItemDelegateCompat; + private final Rect mAvailableBounds = new Rect(100, 200, 300, 400); + private final Position mPlaceholderPosition = new Position(0.0f, 0.0f); + + @Before + public void setUp() { + final WindowManager wm = mContext.getSystemService(WindowManager.class); + doAnswer(invocation -> wm.getMaximumWindowMetrics()).when( + mWindowManager).getMaximumWindowMetrics(); + mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); + + mListView = new RecyclerView(mContext); + mMenuView = + spy(new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition, mListView)); + mItemDelegateCompat = + new ItemDelegateCompat(new RecyclerViewAccessibilityDelegate(mListView), mMenuView); + } + + @Test + public void getAccessibilityActionList_matchResult() { + final AccessibilityNodeInfoCompat info = + new AccessibilityNodeInfoCompat(new AccessibilityNodeInfo()); + + mItemDelegateCompat.onInitializeAccessibilityNodeInfo(mListView, info); + + assertThat(info.getActionList().size()).isEqualTo(5); + } + + @Test + public void performAccessibilityMoveTopLeftAction_halfOval_success() { + doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); + mMenuView.setShapeType(/* halfOvalShape */ 1); + + final boolean moveTopLeftAction = + mItemDelegateCompat.performAccessibilityAction(mListView, R.id.action_move_top_left, + null); + + assertThat(moveTopLeftAction).isTrue(); + assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); + verify(mMenuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.top); + } + + @Test + public void performAccessibilityMoveTopRightAction_halfOval_success() { + doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); + mMenuView.setShapeType(/* halfOvalShape */ 1); + + final boolean moveTopRightAction = + mItemDelegateCompat.performAccessibilityAction(mListView, + R.id.action_move_top_right, null); + + assertThat(moveTopRightAction).isTrue(); + assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); + verify(mMenuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.top); + } + + @Test + public void performAccessibilityMoveBottomLeftAction_halfOval_success() { + doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); + mMenuView.setShapeType(/* halfOvalShape */ 1); + + final boolean moveBottomLeftAction = + mItemDelegateCompat.performAccessibilityAction(mListView, + R.id.action_move_bottom_left, null); + + assertThat(moveBottomLeftAction).isTrue(); + assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); + verify(mMenuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.bottom); + } + + @Test + public void performAccessibilityMoveBottomRightAction_halfOval_success() { + doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); + mMenuView.setShapeType(/* halfOvalShape */ 1); + + final boolean moveBottomRightAction = + mItemDelegateCompat.performAccessibilityAction(mListView, + R.id.action_move_bottom_right, null); + + assertThat(moveBottomRightAction).isTrue(); + assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); + verify(mMenuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.bottom); + } + + @Test + public void performAccessibilityMoveOutEdgeAction_halfOval_success() { + doReturn(mAvailableBounds).when(mMenuView).getAvailableBounds(); + mMenuView.setShapeType(/* halfOvalShape */ 1); + + final boolean moveOutEdgeAndShowAction = + mItemDelegateCompat.performAccessibilityAction(mListView, + R.id.action_move_out_edge_and_show, null); + + assertThat(moveOutEdgeAndShowAction).isTrue(); + assertThat(mMenuView.mShapeType).isEqualTo(/* ovalShape */ 0); + } + + @Test + public void setupAccessibilityActions_oval_hasActionMoveToEdgeAndHide() { + final AccessibilityNodeInfoCompat info = + new AccessibilityNodeInfoCompat(new AccessibilityNodeInfo()); + mMenuView.setShapeType(/* ovalShape */ 0); + + mItemDelegateCompat.onInitializeAccessibilityNodeInfo(mListView, info); + + assertThat(info.getActionList().stream().anyMatch( + action -> action.getId() == R.id.action_move_to_edge_and_hide)).isTrue(); + } +}