From 55fded376ba6d454b5f337941727f687a4e32289 Mon Sep 17 00:00:00 2001 From: Shamali P Date: Tue, 6 Feb 2024 17:49:37 +0000 Subject: [PATCH] Remove the "expand/collapse" actions on the apps list in 2 pane picker. In a single pane picker, apps list is expandable / collapsable, but, its not applicable for 2-pane variant. in 2-pane one, it should just say "double tap to activate". Also opened b/324073588 to make it clear from a11y perspective that it is two pane and on click, right pane change should be announced. (just like it does in settings with talkback). see screencast/cast/NTQ1Mjk0MjIzOTg1ODY4OHw4OTc5ZjAzNS0wOQ Bug: 319195592 Flag: None Test: WidgetsListHeaderAccessibilityTest & manual. Change-Id: I9ae3fbacc29364bd7459f6e6d85e0a1e919de3f8 --- .../widgets_list_row_header_two_pane.xml | 1 + res/values/attrs.xml | 1 + .../widget/picker/WidgetsListHeader.java | 55 +++++++----- .../WidgetsListHeaderAccessibilityTest.java | 87 +++++++++++++++++++ 4 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java diff --git a/res/layout/widgets_list_row_header_two_pane.xml b/res/layout/widgets_list_row_header_two_pane.xml index c0a6ea8226..bdb2aed604 100644 --- a/res/layout/widgets_list_row_header_two_pane.xml +++ b/res/layout/widgets_list_row_header_two_pane.xml @@ -23,6 +23,7 @@ android:importantForAccessibility="yes" android:focusable="true" launcher:appIconSize="48dp" + launcher:collapsable="false" android:descendantFocusability="afterDescendants" android:background="@drawable/bg_widgets_header_two_pane" > diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 754f0cbe47..4a0b5e8637 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -582,6 +582,7 @@ + diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java index b5e7401e10..3383299136 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java @@ -52,7 +52,11 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd private static final int[] EXPANDED_DRAWABLE_STATE = new int[] {android.R.attr.state_expanded}; private final int mIconSize; - + /** + * Indicates if the header is collapsable. For example, when displayed in a two pane layout, + * widget apps aren't collapsable. + */ + private final boolean mIsCollapsable; @Nullable private HandlerRunnable mIconLoadRequest; @Nullable private Drawable mIconDrawable; @Nullable private WidgetsListDrawableState mListDrawableState; @@ -79,6 +83,7 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0); mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize, grid.iconSizePx); + mIsCollapsable = a.getBoolean(R.styleable.WidgetsListRowHeader_collapsable, true); } @Override @@ -87,32 +92,36 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd mAppIcon = findViewById(R.id.app_icon); mTitle = findViewById(R.id.app_title); mSubtitle = findViewById(R.id.app_subtitle); - setAccessibilityDelegate(new AccessibilityDelegate() { + // Lists that cannot collapse, don't need EXPAND or COLLAPSE accessibility actions. + if (mIsCollapsable) { + setAccessibilityDelegate(new AccessibilityDelegate() { - @Override - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { - if (mIsExpanded) { - info.removeAction(AccessibilityNodeInfo.ACTION_EXPAND); - info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE); - } else { - info.removeAction(AccessibilityNodeInfo.ACTION_COLLAPSE); - info.addAction(AccessibilityNodeInfo.ACTION_EXPAND); + @Override + public void onInitializeAccessibilityNodeInfo(View host, + AccessibilityNodeInfo info) { + if (mIsExpanded) { + info.removeAction(AccessibilityNodeInfo.ACTION_EXPAND); + info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE); + } else { + info.removeAction(AccessibilityNodeInfo.ACTION_COLLAPSE); + info.addAction(AccessibilityNodeInfo.ACTION_EXPAND); + } + super.onInitializeAccessibilityNodeInfo(host, info); } - super.onInitializeAccessibilityNodeInfo(host, info); - } - @Override - public boolean performAccessibilityAction(View host, int action, Bundle args) { - switch (action) { - case AccessibilityNodeInfo.ACTION_EXPAND: - case AccessibilityNodeInfo.ACTION_COLLAPSE: - callOnClick(); - return true; - default: - return super.performAccessibilityAction(host, action, args); + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + switch (action) { + case AccessibilityNodeInfo.ACTION_EXPAND: + case AccessibilityNodeInfo.ACTION_COLLAPSE: + callOnClick(); + return true; + default: + return super.performAccessibilityAction(host, action, args); + } } - } - }); + }); + } } /** Sets the expand toggle to expand / collapse. */ diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java new file mode 100644 index 0000000000..b347f07aca --- /dev/null +++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderAccessibilityTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 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.launcher3.widget.picker; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.FrameLayout; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.launcher3.R; +import com.android.launcher3.util.ActivityContextWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WidgetsListHeaderAccessibilityTest { + private Context mContext; + private LayoutInflater mLayoutInflater; + @Mock + private View.OnClickListener mOnClickListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = new ActivityContextWrapper(getApplicationContext()); + mLayoutInflater = LayoutInflater.from( + new ContextThemeWrapper(mContext, R.style.WidgetContainerTheme)); + } + + @Test + public void singlePaneCollapsable_hasCustomAccessibilityActions() { + WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate( + R.layout.widgets_list_row_header, + new FrameLayout(mContext), false); + + assertThat(header.getAccessibilityDelegate()).isNotNull(); + + header.setOnClickListener(mOnClickListener); + header.getAccessibilityDelegate().performAccessibilityAction(header, + AccessibilityNodeInfo.ACTION_EXPAND, null); + header.getAccessibilityDelegate().performAccessibilityAction(header, + AccessibilityNodeInfo.ACTION_COLLAPSE, null); + + verify(mOnClickListener, times(2)).onClick(header); + } + + @Test + public void twoPaneNonCollapsable_noCustomAccessibilityDelegate() { + WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate( + R.layout.widgets_list_row_header_two_pane, + new FrameLayout(mContext), false); + + assertThat(header.getAccessibilityDelegate()).isNull(); + } +}