diff options
author | 2025-01-10 11:28:37 -0800 | |
---|---|---|
committer | 2025-01-14 14:38:52 -0800 | |
commit | b4558f65a5cbd709678d1368b94c4e66167bca87 (patch) | |
tree | 466efea44d064a92b312561a398489453286cbde /java | |
parent | effd3e6c1bba8d5d5ce8047b9a1e455816fcd4b3 (diff) |
Present target grid as hierarchical for a11y purposes.
Present target grid as hierarchical for a11y purposes. This makes
TalkBack to announce sub-groups of targets.
Fix: 379208685
Test: Manual test for the following cases: (i) all group present
(shortcuts, suggested, all-apps), (ii) shortcuts are missing -- the
no-target message is shown, (iii) no suggested targets.
Flag: com.android.intentresolver.announce_shortcuts_and_suggested_apps
Change-Id: If08e9bcccab9db423c5b67c759bc0854d290e880
Diffstat (limited to 'java')
-rw-r--r-- | java/res/layout/chooser_row.xml | 1 | ||||
-rw-r--r-- | java/res/layout/chooser_row_direct_share.xml | 1 | ||||
-rw-r--r-- | java/res/values/strings.xml | 10 | ||||
-rw-r--r-- | java/src/com/android/intentresolver/ChooserGridLayoutManager.java | 131 |
4 files changed, 143 insertions, 0 deletions
diff --git a/java/res/layout/chooser_row.xml b/java/res/layout/chooser_row.xml index bbe65a85..3fe1ee7d 100644 --- a/java/res/layout/chooser_row.xml +++ b/java/res/layout/chooser_row.xml @@ -18,6 +18,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:id="@+id/suggested_apps_container" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="100dp" diff --git a/java/res/layout/chooser_row_direct_share.xml b/java/res/layout/chooser_row_direct_share.xml index d7e36eed..53e666a6 100644 --- a/java/res/layout/chooser_row_direct_share.xml +++ b/java/res/layout/chooser_row_direct_share.xml @@ -17,6 +17,7 @@ */ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/shortcuts_container" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="200dp"> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 2261a4a8..6504462f 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -340,4 +340,14 @@ <string name="selectable_item">Selectable item</string> <!-- Accessibility role description for a11y on button. [CHAR LIMIT=NONE] --> <string name="role_description_button">Button</string> + + <!-- Accessibility announcement for the shortcut group (https://developer.android.com/training/sharing/direct-share-targets) + in the list of targets. [CHAR LIMIT=NONE]--> + <string name="shortcut_group_a11y_title">Direct share targets</string> + <!-- Accessibility announcement for the suggested application group in the list of targets. + [CHAR LIMIT=NONE] --> + <string name="suggested_apps_group_a11y_title">App suggestions</string> + <!-- Accessibility announcement for the all-applications group in the list of targets. + [CHAR LIMIT=NONE] --> + <string name="all_apps_group_a11y_title">App list</string> </resources> diff --git a/java/src/com/android/intentresolver/ChooserGridLayoutManager.java b/java/src/com/android/intentresolver/ChooserGridLayoutManager.java index aaa7554c..5bbb6c24 100644 --- a/java/src/com/android/intentresolver/ChooserGridLayoutManager.java +++ b/java/src/com/android/intentresolver/ChooserGridLayoutManager.java @@ -16,18 +16,35 @@ package com.android.intentresolver; +import static com.android.intentresolver.Flags.announceShortcutsAndSuggestedApps; + import android.content.Context; import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.GridView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.android.intentresolver.grid.ChooserGridAdapter; + /** * For a11y and per {@link RecyclerView#onInitializeAccessibilityNodeInfo}, override * methods to ensure proper row counts. */ public class ChooserGridLayoutManager extends GridLayoutManager { + private CharSequence mShortcutGroupTitle = ""; + private CharSequence mSuggestedAppsGroupTitle = ""; + private CharSequence mAllAppListGroupTitle = ""; + @Nullable + private RecyclerView mRecyclerView; private boolean mVerticalScrollEnabled = true; /** @@ -39,6 +56,9 @@ public class ChooserGridLayoutManager extends GridLayoutManager { public ChooserGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + if (announceShortcutsAndSuggestedApps()) { + readGroupTitles(context); + } } /** @@ -49,6 +69,9 @@ public class ChooserGridLayoutManager extends GridLayoutManager { */ public ChooserGridLayoutManager(Context context, int spanCount) { super(context, spanCount); + if (announceShortcutsAndSuggestedApps()) { + readGroupTitles(context); + } } /** @@ -61,6 +84,27 @@ public class ChooserGridLayoutManager extends GridLayoutManager { public ChooserGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { super(context, spanCount, orientation, reverseLayout); + if (announceShortcutsAndSuggestedApps()) { + readGroupTitles(context); + } + } + + private void readGroupTitles(Context context) { + mShortcutGroupTitle = context.getString(R.string.shortcut_group_a11y_title); + mSuggestedAppsGroupTitle = context.getString(R.string.suggested_apps_group_a11y_title); + mAllAppListGroupTitle = context.getString(R.string.all_apps_group_a11y_title); + } + + @Override + public void onAttachedToWindow(RecyclerView view) { + super.onAttachedToWindow(view); + mRecyclerView = view; + } + + @Override + public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { + super.onDetachedFromWindow(view, recycler); + mRecyclerView = null; } @Override @@ -78,4 +122,91 @@ public class ChooserGridLayoutManager extends GridLayoutManager { public boolean canScrollVertically() { return mVerticalScrollEnabled && super.canScrollVertically(); } + + @Override + public void onInitializeAccessibilityNodeInfoForItem( + RecyclerView.Recycler recycler, + RecyclerView.State state, + View host, + AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info); + if (announceShortcutsAndSuggestedApps() && host instanceof ViewGroup) { + if (host.getId() == R.id.shortcuts_container) { + info.setClassName(GridView.class.getName()); + info.setContainerTitle(mShortcutGroupTitle); + info.setCollectionInfo(createShortcutsA11yCollectionInfo((ViewGroup) host)); + } else if (host.getId() == R.id.suggested_apps_container) { + RecyclerView.Adapter adapter = + mRecyclerView == null ? null : mRecyclerView.getAdapter(); + ChooserListAdapter gridAdapter = adapter instanceof ChooserGridAdapter + ? ((ChooserGridAdapter) adapter).getListAdapter() + : null; + info.setClassName(GridView.class.getName()); + info.setCollectionInfo(createSuggestedAppsA11yCollectionInfo((ViewGroup) host)); + if (gridAdapter == null || gridAdapter.getAlphaTargetCount() > 0) { + info.setContainerTitle(mSuggestedAppsGroupTitle); + } else { + // if all applications fit into one row, they will be put into the suggested + // applications group. + info.setContainerTitle(mAllAppListGroupTitle); + } + } + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler, + @NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(recycler, state, info); + if (announceShortcutsAndSuggestedApps()) { + info.setContainerTitle(mAllAppListGroupTitle); + } + } + + @Override + public boolean isLayoutHierarchical( + @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) { + return announceShortcutsAndSuggestedApps() || super.isLayoutHierarchical(recycler, state); + } + + private CollectionInfoCompat createShortcutsA11yCollectionInfo(ViewGroup container) { + // TODO: create a custom view for the shortcuts row and move this logic there. + int rowCount = 0; + int columnCount = 0; + for (int i = 0; i < container.getChildCount(); i++) { + View row = container.getChildAt(i); + int rowColumnCount = 0; + if (row instanceof ViewGroup rowGroup && row.getVisibility() == View.VISIBLE) { + for (int j = 0; j < rowGroup.getChildCount(); j++) { + View v = rowGroup.getChildAt(j); + if (v != null && v.getVisibility() == View.VISIBLE) { + rowColumnCount++; + if (v instanceof TextView) { + // A special case of the no-targets message that also contains an + // off-screen item (which looks like a bug). + rowColumnCount = 1; + break; + } + } + } + } + if (rowColumnCount > 0) { + rowCount++; + columnCount = Math.max(columnCount, rowColumnCount); + } + } + return CollectionInfoCompat.obtain(rowCount, columnCount, false); + } + + private CollectionInfoCompat createSuggestedAppsA11yCollectionInfo(ViewGroup container) { + // TODO: create a custom view for the suggested apps row and move this logic there. + int columnCount = 0; + for (int i = 0; i < container.getChildCount(); i++) { + View v = container.getChildAt(i); + if (v.getVisibility() == View.VISIBLE) { + columnCount++; + } + } + return CollectionInfoCompat.obtain(1, columnCount, false); + } } |