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

Commit f1f8f318 authored by Alan Viverette's avatar Alan Viverette Committed by Android (Google) Code Review
Browse files

Merge "Set transient state for focus container in ListView"

parents f889c84f d44696c4
Loading
Loading
Loading
Loading
+42 −33
Original line number Original line 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 scrap The view to add
         * @param position The view's position within its parent
         */
         */
        void addScrapView(View scrap, int position) {
        void addScrapView(View scrap, int position) {
            AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
            final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
            if (lp == null) {
            if (lp == null) {
                return;
                return;
            }
            }


            lp.scrappedFromPosition = position;
            lp.scrappedFromPosition = position;


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

            scrap.dispatchStartTemporaryDetach();
            scrap.dispatchStartTemporaryDetach();

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

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


                scrap.setAccessibilityDelegate(null);
                scrap.setAccessibilityDelegate(null);

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


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


        mBlockLayoutRequests = true;

        try {
        try {
            super.layoutChildren();
            super.layoutChildren();


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


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


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


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


            View focusLayoutRestoreView = null;

            AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
            AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
            View accessibilityFocusLayoutRestoreView = null;
            View accessibilityFocusLayoutRestoreView = null;
            int accessibilityFocusPosition = INVALID_POSITION;
            int accessibilityFocusPosition = INVALID_POSITION;
@@ -1570,6 +1568,7 @@ public class ListView extends AbsListView {
            // Remember which child, if any, had accessibility focus. This must
            // Remember which child, if any, had accessibility focus. This must
            // occur before recycling any views, since that will clear
            // occur before recycling any views, since that will clear
            // accessibility focus.
            // accessibility focus.
            // TODO: This should rely on transient state.
            final ViewRootImpl viewRootImpl = getViewRootImpl();
            final ViewRootImpl viewRootImpl = getViewRootImpl();
            if (viewRootImpl != null) {
            if (viewRootImpl != null) {
                final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
                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.
            // Pull all children into the RecycleBin.
            // These views will be reused if possible
            // These views will be reused if possible
            final int firstPosition = mFirstPosition;
            final int firstPosition = mFirstPosition;
            final RecycleBin recycleBin = mRecycler;
            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) {
            if (dataChanged) {
                for (int i = 0; i < childCount; i++) {
                for (int i = 0; i < childCount; i++) {
                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
@@ -1611,28 +1612,6 @@ public class ListView extends AbsListView {
                recycleBin.fillActiveViews(childCount, firstPosition);
                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
            // Clear out old views
            detachAllViewsFromParent();
            detachAllViewsFromParent();
            recycleBin.removeSkippedScrap();
            recycleBin.removeSkippedScrap();
@@ -1692,43 +1671,37 @@ public class ListView extends AbsListView {
            recycleBin.scrapActiveViews();
            recycleBin.scrapActiveViews();


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