Fix TB lose focus when navigating recyclerview
- Issue history: b/37088814 and previous workaround: ag/11747784
- Since the previous workaround no longer works, apply new scroll
behavior to the recyclerview when TB navigating, make sure the focusing item's
previous/next item totally visible, then TB won't lose focus.
video: https://drive.google.com/file/d/1KJQT9cAmm9G5yZGDZZTNnpezDbT_Rh31/view?usp=sharing
Bug: 157007291
Test: manually, enable TB and test the result
Change-Id: I1c4c8416d7b1849cf705db931bcd9c6e5c354719
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
index a532e40..69a45a9 100644
--- a/src/com/android/customization/widget/OptionSelectorController.java
+++ b/src/com/android/customization/widget/OptionSelectorController.java
@@ -26,12 +26,14 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import com.android.customization.model.CustomizationManager;
import com.android.customization.model.CustomizationOption;
@@ -154,17 +156,15 @@
* Initializes the UI for the options passed in the constructor of this class.
*/
public void initOptions(final CustomizationManager<T> manager) {
+ mContainer.setAccessibilityDelegateCompat(
+ new OptionSelectorAccessibilityDelegate(mContainer));
+
mAdapter = new RecyclerView.Adapter<TileViewHolder>() {
@Override
public int getItemViewType(int position) {
return mOptions.get(position).getLayoutResId();
}
- @Override
- public long getItemId(int position) {
- return position;
- }
-
@NonNull
@Override
public TileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -232,10 +232,6 @@
LinearLayoutManager.HORIZONTAL, false));
Resources res = mContainer.getContext().getResources();
- // A workaround from b/37088814, fix TalkBack will lose focus when receive notify*Changed()
- mAdapter.setHasStableIds(true);
- mContainer.setItemAnimator(null);
-
mContainer.setAdapter(mAdapter);
// Measure RecyclerView to get to the total amount of space used by all options.
@@ -340,4 +336,40 @@
}
}
}
+
+ private class OptionSelectorAccessibilityDelegate extends RecyclerViewAccessibilityDelegate {
+
+ OptionSelectorAccessibilityDelegate(RecyclerView recyclerView) {
+ super(recyclerView);
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(
+ ViewGroup host, View child, AccessibilityEvent event) {
+
+ // Apply this workaround to horizontal recyclerview only,
+ // since the symptom is TalkBack will lose focus when navigating horizontal list items.
+ if (mContainer.getLayoutManager() != null
+ && mContainer.getLayoutManager().canScrollHorizontally()
+ && event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
+ int itemPos = mContainer.getChildLayoutPosition(child);
+ int itemWidth = mContainer.getContext().getResources()
+ .getDimensionPixelOffset(R.dimen.option_tile_width);
+ int itemMarginHorizontal = mContainer.getContext().getResources()
+ .getDimensionPixelOffset(R.dimen.option_tile_margin_horizontal) * 2;
+ int scrollOffset = itemWidth + itemMarginHorizontal;
+
+ // Make focusing item's previous/next item totally visible when changing focus,
+ // ensure TalkBack won't lose focus when recyclerview scrolling.
+ if (itemPos >= ((LinearLayoutManager) mContainer.getLayoutManager())
+ .findLastCompletelyVisibleItemPosition()) {
+ mContainer.scrollBy(scrollOffset, 0);
+ } else if (itemPos <= ((LinearLayoutManager) mContainer.getLayoutManager())
+ .findFirstCompletelyVisibleItemPosition() && itemPos != 0) {
+ mContainer.scrollBy(-scrollOffset, 0);
+ }
+ }
+ return super.onRequestSendAccessibilityEvent(host, child, event);
+ }
+ }
}