Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49bcc271 authored by Wesley.CW Wang's avatar Wesley.CW Wang
Browse files

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
parent 0e91a892
Loading
Loading
Loading
Loading
+41 −9
Original line number Diff line number Diff line
@@ -26,12 +26,14 @@ import android.view.LayoutInflater;
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 @@ public class OptionSelectorController<T extends CustomizationOption<T>> {
     * 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 @@ public class OptionSelectorController<T extends CustomizationOption<T>> {
                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 @@ public class OptionSelectorController<T extends CustomizationOption<T>> {
            }
        }
    }

    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);
        }
    }
}