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

Commit d44696c4 authored by Alan Viverette's avatar Alan Viverette
Browse files

Set transient state for focus container in ListView

BUG: 9860185
Change-Id: I0c7035e5992c56110a0cc5c94aa778bbb999deea
parent 209bede6
Loading
Loading
Loading
Loading
+42 −33
Original line number Diff line number Diff line
@@ -6500,49 +6500,56 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }

        /**
         * Put a view into the ScrapViews list. These views are unordered.
         * Puts a view into the list of scrap views.
         * <p>
         * If the list data hasn't changed or the adapter has stable IDs, views
         * with transient state will be preserved for later retrieval.
         *
         * @param scrap The view to add
         * @param position The view's position within its parent
         */
        void addScrapView(View scrap, int position) {
            AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
            final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
            if (lp == null) {
                return;
            }

            lp.scrappedFromPosition = position;

            // Don't put header or footer views or views that should be ignored
            // into the scrap heap
            int viewType = lp.viewType;
            final boolean scrapHasTransientState = scrap.hasTransientState();
            if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) {
                    if (mSkippedScrap == null) {
                        mSkippedScrap = new ArrayList<View>();
                    }
                    mSkippedScrap.add(scrap);
            // Don't scrap header or footer views, or views that should
            // otherwise not be recycled.
            final int viewType = lp.viewType;
            if (!shouldRecycleViewType(viewType)) {
                return;
            }
                if (scrapHasTransientState) {

            scrap.dispatchStartTemporaryDetach();

            // Don't scrap views that have transient state.
            final boolean scrapHasTransientState = scrap.hasTransientState();
            if (scrapHasTransientState) {
                if (mAdapter != null && mAdapterHasStableIds) {
                    // If the adapter has stable IDs, we can reuse the view for
                    // the same data.
                    if (mTransientStateViewsById == null) {
                        mTransientStateViewsById = new LongSparseArray<View>();
                    }
                    mTransientStateViewsById.put(lp.itemId, scrap);
                } else if (!mDataChanged) {
                        // avoid putting views on transient state list during a data change;
                        // the layout positions may be out of sync with the adapter positions
                    // If the data hasn't changed, we can reuse the views at
                    // their old positions.
                    if (mTransientStateViews == null) {
                        mTransientStateViews = new SparseArray<View>();
                    }
                    mTransientStateViews.put(position, scrap);
                } else {
                    // Otherwise, we'll have to remove the view and start over.
                    if (mSkippedScrap == null) {
                        mSkippedScrap = new ArrayList<View>();
                    }
                    mSkippedScrap.add(scrap);
                }
                return;
            }

            scrap.dispatchStartTemporaryDetach();
            } else {
                if (mViewTypeCount == 1) {
                    mCurrentScrap.add(scrap);
                } else {
@@ -6550,10 +6557,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                }

                scrap.setAccessibilityDelegate(null);

                if (mRecyclerListener != null) {
                    mRecyclerListener.onMovedToScrapHeap(scrap);
                }
            }
        }

        /**
         * Finish the removal of any views that skipped the scrap heap.
+33 −63
Original line number Diff line number Diff line
@@ -1478,12 +1478,12 @@ public class ListView extends AbsListView {
    @Override
    protected void layoutChildren() {
        final boolean blockLayoutRequests = mBlockLayoutRequests;
        if (!blockLayoutRequests) {
            mBlockLayoutRequests = true;
        } else {
        if (blockLayoutRequests) {
            return;
        }

        mBlockLayoutRequests = true;

        try {
            super.layoutChildren();

@@ -1495,10 +1495,10 @@ public class ListView extends AbsListView {
                return;
            }

            int childrenTop = mListPadding.top;
            int childrenBottom = mBottom - mTop - mListPadding.bottom;
            final int childrenTop = mListPadding.top;
            final int childrenBottom = mBottom - mTop - mListPadding.bottom;
            final int childCount = getChildCount();

            int childCount = getChildCount();
            int index = 0;
            int delta = 0;

@@ -1507,8 +1507,6 @@ public class ListView extends AbsListView {
            View oldFirst = null;
            View newSel = null;

            View focusLayoutRestoreView = null;

            AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
            View accessibilityFocusLayoutRestoreView = null;
            int accessibilityFocusPosition = INVALID_POSITION;
@@ -1570,6 +1568,7 @@ public class ListView extends AbsListView {
            // Remember which child, if any, had accessibility focus. This must
            // occur before recycling any views, since that will clear
            // accessibility focus.
            // TODO: This should rely on transient state.
            final ViewRootImpl viewRootImpl = getViewRootImpl();
            if (viewRootImpl != null) {
                final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
@@ -1593,16 +1592,18 @@ public class ListView extends AbsListView {
                }
            }

            // Ensure the child containing focus, if any, has transient state.
            // If the list data hasn't changed, or if the adapter has stable
            // IDs, this will maintain focus.
            final View focusedChild = getFocusedChild();
            if (focusedChild != null) {
                focusedChild.setHasTransientState(true);
            }

            // Pull all children into the RecycleBin.
            // These views will be reused if possible
            final int firstPosition = mFirstPosition;
            final RecycleBin recycleBin = mRecycler;

            // reset the focus restoration
            View focusLayoutRestoreDirectChild = null;

            // Don't put header or footer views into the Recycler. Those are
            // already cached in mHeaderViews;
            if (dataChanged) {
                for (int i = 0; i < childCount; i++) {
                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
@@ -1611,28 +1612,6 @@ public class ListView extends AbsListView {
                recycleBin.fillActiveViews(childCount, firstPosition);
            }

            // take focus back to us temporarily to avoid the eventual
            // call to clear focus when removing the focused child below
            // from messing things up when ViewAncestor assigns focus back
            // to someone else
            final View focusedChild = getFocusedChild();
            if (focusedChild != null) {
                // TODO: in some cases focusedChild.getParent() == null

                // we can remember the focused view to restore after relayout if the
                // data hasn't changed, or if the focused position is a header or footer
                if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)) {
                    focusLayoutRestoreDirectChild = focusedChild;
                    // remember the specific view that had focus
                    focusLayoutRestoreView = findFocus();
                    if (focusLayoutRestoreView != null) {
                        // tell it we are going to mess with it
                        focusLayoutRestoreView.onStartTemporaryDetach();
                    }
                }
                requestFocus();
            }

            // Clear out old views
            detachAllViewsFromParent();
            recycleBin.removeSkippedScrap();
@@ -1692,43 +1671,37 @@ public class ListView extends AbsListView {
            recycleBin.scrapActiveViews();

            if (sel != null) {
                // the current selected item should get focus if items
                // are focusable
                if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) {
                    final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild &&
                            focusLayoutRestoreView != null &&
                            focusLayoutRestoreView.requestFocus()) || sel.requestFocus();
                    if (!focusWasTaken) {
                        // selected item didn't take focus, fine, but still want
                        // to make sure something else outside of the selected view
                        // has focus
                final boolean shouldPlaceFocus = mItemsCanFocus && hasFocus();
                final boolean maintainedFocus = focusedChild != null && focusedChild.hasFocus();
                if (shouldPlaceFocus && !maintainedFocus && !sel.hasFocus()) {
                    if (sel.requestFocus()) {
                        // Successfully placed focus, clear selection.
                        sel.setSelected(false);
                        mSelectorRect.setEmpty();
                    } else {
                        // Failed to place focus, clear current (invalid) focus.
                        final View focused = getFocusedChild();
                        if (focused != null) {
                            focused.clearFocus();
                        }
                        positionSelector(INVALID_POSITION, sel);
                    } else {
                        sel.setSelected(false);
                        mSelectorRect.setEmpty();
                    }
                } else {
                    positionSelector(INVALID_POSITION, sel);
                }
                mSelectedTop = sel.getTop();
            } else {
                if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
                    View child = getChildAt(mMotionPosition - mFirstPosition);
                    if (child != null) positionSelector(mMotionPosition, child);
                // If the user's finger is down, select the motion position.
                // Otherwise, clear selection.
                if (mTouchMode == TOUCH_MODE_TAP || mTouchMode == TOUCH_MODE_DONE_WAITING) {
                    final View child = getChildAt(mMotionPosition - mFirstPosition);
                    if (child != null)  {
                        positionSelector(mMotionPosition, child);
                    }
                } else {
                    mSelectedTop = 0;
                    mSelectorRect.setEmpty();
                }

                // even if there is not selected position, we may need to restore
                // focus (i.e. something focusable in touch mode)
                if (hasFocus() && focusLayoutRestoreView != null) {
                    focusLayoutRestoreView.requestFocus();
                }
            }

            // Attempt to restore accessibility focus.
@@ -1753,11 +1726,8 @@ public class ListView extends AbsListView {
                }
            }

            // tell focus view we are done mucking with it, if it is still in
            // our view hierarchy.
            if (focusLayoutRestoreView != null
                    && focusLayoutRestoreView.getWindowToken() != null) {
                focusLayoutRestoreView.onFinishTemporaryDetach();
            if (focusedChild != null) {
                focusedChild.setHasTransientState(false);
            }
            
            mLayoutMode = LAYOUT_NORMAL;