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

Commit 30ee76cf authored by alanv's avatar alanv
Browse files

Restore accessibility focus after ListView layout.

Bug: 6439454
Change-Id: Ia61f5153b32c6ce5d18301f74e7e79c86349b987
parent 5851c6e6
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.SparseBooleanArray;
import android.view.FocusFinder;
import android.view.KeyEvent;
@@ -1490,6 +1491,10 @@ public class ListView extends AbsListView {

            View focusLayoutRestoreView = null;

            AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
            View accessibilityFocusLayoutRestoreView = null;
            int accessibilityFocusPosition = INVALID_POSITION;

            // Remember stuff we will need down below
            switch (mLayoutMode) {
            case LAYOUT_SET_SELECTION:
@@ -1584,6 +1589,25 @@ public class ListView extends AbsListView {
                requestFocus();
            }

            // Remember which child, if any, had accessibility focus.
            final View accessFocusedView = getViewRootImpl().getAccessibilityFocusedHost();
            if (accessFocusedView != null) {
                final View accessFocusedChild = findAccessibilityFocusedChild(accessFocusedView);
                if (accessFocusedChild != null) {
                    if (!dataChanged || isDirectChildHeaderOrFooter(accessFocusedChild)) {
                        // If the views won't be changing, try to maintain focus
                        // on the current view host and (if applicable) its
                        // virtual view.
                        accessibilityFocusLayoutRestoreView = accessFocusedView;
                        accessibilityFocusLayoutRestoreNode = getViewRootImpl()
                                .getAccessibilityFocusedVirtualView();
                    } else {
                        // Otherwise, try to maintain focus at the same position.
                        accessibilityFocusPosition = getPositionForView(accessFocusedChild);
                    }
                }
            }

            // Clear out old views
            detachAllViewsFromParent();
            recycleBin.removeSkippedScrap();
@@ -1682,6 +1706,22 @@ public class ListView extends AbsListView {
                }
            }

            // Attempt to restore accessibility focus.
            if (accessibilityFocusLayoutRestoreNode != null) {
                accessibilityFocusLayoutRestoreNode.performAction(
                        AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
            } else if (accessibilityFocusLayoutRestoreView != null) {
                accessibilityFocusLayoutRestoreView.requestAccessibilityFocus();
            } else if (accessibilityFocusPosition != INVALID_POSITION) {
                // Bound the position within the visible children.
                final int position = MathUtils.constrain(
                        (accessibilityFocusPosition - mFirstPosition), 0, (getChildCount() - 1));
                final View restoreView = getChildAt(position);
                if (restoreView != null) {
                    restoreView.requestAccessibilityFocus();
                }
            }

            // tell focus view we are done mucking with it, if it is still in
            // our view hierarchy.
            if (focusLayoutRestoreView != null
@@ -1712,6 +1752,22 @@ public class ListView extends AbsListView {
        }
    }

    /**
     * @param focusedView the view that has accessibility focus.
     * @return the direct child that contains accessibility focus.
     */
    private View findAccessibilityFocusedChild(View focusedView) {
        ViewParent viewParent = focusedView.getParent();
        while ((viewParent instanceof View) && (viewParent != this)) {
            focusedView = (View) viewParent;
            viewParent = viewParent.getParent();
        }
        if (!(viewParent instanceof View)) {
            return null;
        }
        return focusedView;
    }

    /**
     * @param child a direct child of this list.
     * @return Whether child is a header or footer view.