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

Commit 9ffcbcc1 authored by Adam Powell's avatar Adam Powell Committed by Android (Google) Code Review
Browse files

Merge "Add transient state tracking to Views"

parents 5bf842b2 539ee871
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -1759,6 +1759,16 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
    static final int LAYOUT_DIRECTION_RESOLVED = 0x00000008;
    /**
     * Indicates that the view is tracking some sort of transient state
     * that the app should not need to be aware of, but that the framework
     * should take special care to preserve.
     *
     * @hide
     */
    static final int HAS_TRANSIENT_STATE = 0x00000010;
    /* End of masks for mPrivateFlags2 */
    static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
@@ -4888,6 +4898,43 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
        return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL);
    }
    /**
     * Indicates whether the view is currently tracking transient state that the
     * app should not need to concern itself with saving and restoring, but that
     * the framework should take special note to preserve when possible.
     *
     * @return true if the view has transient state
     *
     * @hide
     */
    @ViewDebug.ExportedProperty(category = "layout")
    public boolean hasTransientState() {
        return (mPrivateFlags2 & HAS_TRANSIENT_STATE) == HAS_TRANSIENT_STATE;
    }
    /**
     * Set whether this view is currently tracking transient state that the
     * framework should attempt to preserve when possible.
     *
     * @param hasTransientState true if this view has transient state
     *
     * @hide
     */
    public void setHasTransientState(boolean hasTransientState) {
        if (hasTransientState() == hasTransientState) return;
        mPrivateFlags2 = (mPrivateFlags2 & ~HAS_TRANSIENT_STATE) |
                (hasTransientState ? HAS_TRANSIENT_STATE : 0);
        if (mParent != null) {
            try {
                mParent.childHasTransientStateChanged(this, hasTransientState);
            } catch (AbstractMethodError e) {
                Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
                        " does not fully implement ViewParent", e);
            }
        }
    }
    /**
     * If this view doesn't do any drawing on its own, set this flag to
     * allow further optimizations. By default, this flag is not set on
+57 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import java.util.HashSet;
 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
 */
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    private static final String TAG = "ViewGroup";

    private static final boolean DBG = false;

@@ -375,6 +376,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
    @ViewDebug.ExportedProperty(category = "drawing")
    boolean mDrawLayers = true;

    // Indicates how many of this container's child subtrees contain transient state
    @ViewDebug.ExportedProperty(category = "layout")
    private int mChildCountWithTransientState = 0;

    public ViewGroup(Context context) {
        super(context);
        initViewGroup();
@@ -652,6 +657,38 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return true;
    }

    /**
     * Called when a child view has changed whether or not it is tracking transient state.
     *
     * @hide
     */
    public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
        final boolean oldHasTransientState = hasTransientState();
        if (childHasTransientState) {
            mChildCountWithTransientState++;
        } else {
            mChildCountWithTransientState--;
        }

        final boolean newHasTransientState = hasTransientState();
        if (mParent != null && oldHasTransientState != newHasTransientState) {
            try {
                mParent.childHasTransientStateChanged(this, newHasTransientState);
            } catch (AbstractMethodError e) {
                Log.e(TAG, mParent.getClass().getSimpleName() +
                        " does not fully implement ViewParent", e);
            }
        }
    }

    /**
     * @hide
     */
    @Override
    public boolean hasTransientState() {
        return mChildCountWithTransientState > 0 || super.hasTransientState();
    }

    /**
     * {@inheritDoc}
     */
@@ -3099,6 +3136,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
        }

        if (child.hasTransientState()) {
            childHasTransientStateChanged(child, true);
        }
    }

    private void addInArray(View child, int index) {
@@ -3295,6 +3336,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
           view.dispatchDetachedFromWindow();
        }

        if (view.hasTransientState()) {
            childHasTransientStateChanged(view, false);
        }

        onViewRemoved(view);

        needGlobalAttributesUpdate(false);
@@ -3366,6 +3411,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
               view.dispatchDetachedFromWindow();
            }

            if (view.hasTransientState()) {
                childHasTransientStateChanged(view, false);
            }

            needGlobalAttributesUpdate(false);

            onViewRemoved(view);
@@ -3431,6 +3480,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
               view.dispatchDetachedFromWindow();
            }

            if (view.hasTransientState()) {
                childHasTransientStateChanged(view, false);
            }

            onViewRemoved(view);

            view.mParent = null;
@@ -3471,6 +3524,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            child.dispatchDetachedFromWindow();
        }

        if (child.hasTransientState()) {
            childHasTransientStateChanged(child, false);
        }

        onViewRemoved(child);
    }

+10 −0
Original line number Diff line number Diff line
@@ -261,4 +261,14 @@ public interface ViewParent {
     * @return True if the event was sent.
     */
    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event);

    /**
     * Called when a child view now has or no longer is tracking transient state.
     *
     * @param child Child view whose state has changed
     * @param hasTransientState true if this child has transient state
     *
     * @hide
     */
    public void childHasTransientStateChanged(View child, boolean hasTransientState);
}
+4 −0
Original line number Diff line number Diff line
@@ -4144,6 +4144,10 @@ public final class ViewRootImpl implements ViewParent,
        return scrollToRectOrFocus(rectangle, immediate);
    }

    public void childHasTransientStateChanged(View child, boolean hasTransientState) {
        // Do nothing.
    }

    class TakenSurfaceHolder extends BaseSurfaceHolder {
        @Override
        public boolean onAllowLockCanvas() {
+108 −11
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.StateSet;
import android.view.ActionMode;
@@ -87,6 +88,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        ViewTreeObserver.OnTouchModeChangeListener,
        RemoteViewsAdapter.RemoteAdapterConnectionCallback {

    private static final String TAG = "AbsListView";

    /**
     * Disables the transcript mode.
     *
@@ -262,6 +265,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
     */
    private RemoteViewsAdapter mRemoteAdapter;

    /**
     * If mAdapter != null, whenever this is true the adapter has stable IDs.
     */
    boolean mAdapterHasStableIds;

    /**
     * This flag indicates the a full notify is required when the RemoteViewsAdapter connects
     */
@@ -812,7 +820,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
    @Override
    public void setAdapter(ListAdapter adapter) {
        if (adapter != null) {
            if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() &&
            mAdapterHasStableIds = mAdapter.hasStableIds();
            if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds &&
                    mCheckedIdStates == null) {
                mCheckedIdStates = new LongSparseArray<Integer>();
            }
@@ -2011,6 +2020,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        isScrap[0] = false;
        View scrapView;

        scrapView = mRecycler.getTransientStateView(position);
        if (scrapView != null) {
            return scrapView;
        }

        scrapView = mRecycler.getScrapView(position);

        View child;
@@ -2021,6 +2035,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            }

            child = mAdapter.getView(position, scrapView, this);
            if (mAdapterHasStableIds) {
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (lp == null) {
                    lp = (LayoutParams) generateDefaultLayoutParams();
                }
                lp.itemId = mAdapter.getItemId(position);
            }

            if (ViewDebug.TRACE_RECYCLER) {
                ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
@@ -4543,7 +4564,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

        if (count > 0) {
            detachViewsFromParent(start, count);
            mRecycler.removeSkippedScrap();
        }

        offsetChildrenTopAndBottom(incrementalDeltaY);

        if (down) {
@@ -4853,6 +4876,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            confirmCheckedPositionsById();
        }

        // TODO: In the future we can recycle these views based on stable ID instead.
        mRecycler.clearTransientStateViews();

        if (count > 0) {
            int newPos;
            int selectablePos;
@@ -5735,6 +5761,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
         */
        int scrappedFromPosition;

        /**
         * The ID the view represents
         */
        long itemId = -1;

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }
@@ -5807,6 +5838,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

        private ArrayList<View> mCurrentScrap;

        private ArrayList<View> mSkippedScrap;

        private SparseArray<View> mTransientStateViews;

        public void setViewTypeCount(int viewTypeCount) {
            if (viewTypeCount < 1) {
                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
@@ -5838,6 +5873,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                    }
                }
            }
            if (mTransientStateViews != null) {
                final int count = mTransientStateViews.size();
                for (int i = 0; i < count; i++) {
                    mTransientStateViews.valueAt(i).forceLayout();
                }
            }
        }

        public boolean shouldRecycleViewType(int viewType) {
@@ -5864,6 +5905,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                    }
                }
            }
            if (mTransientStateViews != null) {
                mTransientStateViews.clear();
            }
        }

        /**
@@ -5910,6 +5954,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            return null;
        }

        View getTransientStateView(int position) {
            if (mTransientStateViews == null) {
                return null;
            }
            final int index = mTransientStateViews.indexOfKey(position);
            if (index < 0) {
                return null;
            }
            final View result = mTransientStateViews.valueAt(index);
            mTransientStateViews.removeAt(index);
            return result;
        }

        /**
         * Dump any currently saved views with transient state.
         */
        void clearTransientStateViews() {
            if (mTransientStateViews != null) {
                mTransientStateViews.clear();
            }
        }

        /**
         * @return A view from the ScrapViews collection. These are unordered.
         */
@@ -5926,7 +5992,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }

        /**
         * Put a view into the ScapViews list. These views are unordered.
         * Put a view into the ScrapViews list. These views are unordered.
         *
         * @param scrap The view to add
         */
@@ -5936,23 +6002,32 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                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;
            if (!shouldRecycleViewType(viewType)) {
                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                    removeDetachedView(scrap, false);
            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);
                }
                if (scrapHasTransientState) {
                    if (mTransientStateViews == null) {
                        mTransientStateViews = new SparseArray<View>();
                    }
                    mTransientStateViews.put(position, scrap);
                }
                return;
            }

            lp.scrappedFromPosition = position;

            if (mViewTypeCount == 1) {
            scrap.dispatchStartTemporaryDetach();
            if (mViewTypeCount == 1) {
                mCurrentScrap.add(scrap);
            } else {
                scrap.dispatchStartTemporaryDetach();
                mScrapViews[viewType].add(scrap);
            }

@@ -5961,6 +6036,20 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            }
        }

        /**
         * Finish the removal of any views that skipped the scrap heap.
         */
        void removeSkippedScrap() {
            if (mSkippedScrap == null) {
                return;
            }
            final int count = mSkippedScrap.size();
            for (int i = 0; i < count; i++) {
                removeDetachedView(mSkippedScrap.get(i), false);
            }
            mSkippedScrap.clear();
        }

        /**
         * Move all views remaining in mActiveViews to mScrapViews.
         */
@@ -5980,11 +6069,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

                    activeViews[i] = null;

                    if (!shouldRecycleViewType(whichScrap)) {
                    final boolean scrapHasTransientState = victim.hasTransientState();
                    if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) {
                        // Do not move views that should be ignored
                        if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                        if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER ||
                                scrapHasTransientState) {
                            removeDetachedView(victim, false);
                        }
                        if (scrapHasTransientState) {
                            if (mTransientStateViews == null) {
                                mTransientStateViews = new SparseArray<View>();
                            }
                            mTransientStateViews.put(mFirstActivePosition + i, victim);
                        }
                        continue;
                    }

Loading