17149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas/*
27149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Copyright (C) 2017 The Android Open Source Project
37149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *
47149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Licensed under the Apache License, Version 2.0 (the "License");
57149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * you may not use this file except in compliance with the License.
67149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * You may obtain a copy of the License at
77149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *
87149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *      http://www.apache.org/licenses/LICENSE-2.0
97149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *
107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Unless required by applicable law or agreed to in writing, software
117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * distributed under the License is distributed on an "AS IS" BASIS,
127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * See the License for the specific language governing permissions and
147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * limitations under the License.
157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */
167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikaspackage com.android.internal.widget;
187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.annotation.Nullable;
207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.os.Trace;
217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.view.View;
227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.ArrayList;
247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.Arrays;
257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.Collections;
267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.Comparator;
277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.concurrent.TimeUnit;
287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasfinal class GapWorker implements Runnable {
307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>();
347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    long mPostTimeNs;
357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    long mFrameIntervalNs;
367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static class Task {
387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public boolean immediate;
397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public int viewVelocity;
407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public int distanceToItem;
417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public RecyclerView view;
427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public int position;
437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public void clear() {
457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            immediate = false;
467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            viewVelocity = 0;
477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            distanceToItem = 0;
487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            view = null;
497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            position = 0;
507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects
557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared
567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * in between calls.
577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private ArrayList<Task> mTasks = new ArrayList<>();
597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * Prefetch information associated with a specific RecyclerView.
627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static class LayoutPrefetchRegistryImpl
647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            implements RecyclerView.LayoutManager.LayoutPrefetchRegistry {
657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int mPrefetchDx;
667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int mPrefetchDy;
677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int[] mPrefetchArray;
687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int mCount;
707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void setPrefetchVector(int dx, int dy) {
727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mPrefetchDx = dx;
737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mPrefetchDy = dy;
747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) {
777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mCount = 0;
787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mPrefetchArray != null) {
797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Arrays.fill(mPrefetchArray, -1);
807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            final RecyclerView.LayoutManager layout = view.mLayout;
837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (view.mAdapter != null
847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    && layout != null
857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    && layout.isItemPrefetchEnabled()) {
867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (nested) {
877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // nested prefetch, only if no adapter updates pending. Note: we don't query
887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // view.hasPendingAdapterUpdates(), as first layout may not have occurred
897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (!view.mAdapterHelper.hasPendingUpdates()) {
907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this);
917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                } else {
937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // momentum based prefetch, only if we trust current child/adapter state
947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (!view.hasPendingAdapterUpdates()) {
957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy,
967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                                view.mState, this);
977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (mCount > layout.mPrefetchMaxCountObserved) {
1017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    layout.mPrefetchMaxCountObserved = mCount;
1027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    layout.mPrefetchMaxObservedInInitialPrefetch = nested;
1037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    view.mRecycler.updateViewCacheSize();
1047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
1057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        @Override
1097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public void addPosition(int layoutPosition, int pixelDistance) {
1107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (pixelDistance < 0) {
1117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                throw new IllegalArgumentException("Pixel distance must be non-negative");
1127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // allocate or expand array as needed, doubling when needed
1157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            final int storagePosition = mCount * 2;
1167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mPrefetchArray == null) {
1177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mPrefetchArray = new int[4];
1187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Arrays.fill(mPrefetchArray, -1);
1197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else if (storagePosition >= mPrefetchArray.length) {
1207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                final int[] oldArray = mPrefetchArray;
1217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mPrefetchArray = new int[storagePosition * 2];
1227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length);
1237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // add position
1267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mPrefetchArray[storagePosition] = layoutPosition;
1277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mPrefetchArray[storagePosition + 1] = pixelDistance;
1287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mCount++;
1307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        boolean lastPrefetchIncludedPosition(int position) {
1337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mPrefetchArray != null) {
1347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                final int count = mCount * 2;
1357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                for (int i = 0; i < count; i += 2) {
1367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (mPrefetchArray[i] == position) return true;
1377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
1387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return false;
1407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        /**
1437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas         * Called when prefetch indices are no longer valid for cache prioritization.
1447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas         */
1457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void clearPrefetchPositions() {
1467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mPrefetchArray != null) {
1477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Arrays.fill(mPrefetchArray, -1);
1487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    public void add(RecyclerView recyclerView) {
1537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) {
1547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            throw new IllegalStateException("RecyclerView already present in worker list!");
1557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mRecyclerViews.add(recyclerView);
1577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    public void remove(RecyclerView recyclerView) {
1607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        boolean removeSuccess = mRecyclerViews.remove(recyclerView);
1617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (RecyclerView.DEBUG && !removeSuccess) {
1627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            throw new IllegalStateException("RecyclerView removal failed!");
1637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
1677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * Schedule a prefetch immediately after the current traversal.
1687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
1697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
1707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (recyclerView.isAttachedToWindow()) {
1717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) {
1727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                throw new IllegalStateException("attempting to post unregistered view!");
1737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mPostTimeNs == 0) {
1757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mPostTimeNs = recyclerView.getNanoTime();
1767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                recyclerView.post(this);
1777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
1817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static Comparator<Task> sTaskComparator = new Comparator<Task>() {
1847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        @Override
1857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public int compare(Task lhs, Task rhs) {
1867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // first, prioritize non-cleared tasks
1877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if ((lhs.view == null) != (rhs.view == null)) {
1887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return lhs.view == null ? 1 : -1;
1897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // then prioritize immediate
1927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (lhs.immediate != rhs.immediate) {
1937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return lhs.immediate ? -1 : 1;
1947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // then prioritize _highest_ view velocity
1977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity;
1987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (deltaViewVelocity != 0) return deltaViewVelocity;
1997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // then prioritize _lowest_ distance to item
2017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem;
2027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (deltaDistanceToItem != 0) return deltaDistanceToItem;
2037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return 0;
2057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    };
2077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void buildTaskList() {
2097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // Update PrefetchRegistry in each view
2107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int viewCount = mRecyclerViews.size();
2117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int totalTaskCount = 0;
2127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < viewCount; i++) {
2137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            RecyclerView view = mRecyclerViews.get(i);
2147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false);
2157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            totalTaskCount += view.mPrefetchRegistry.mCount;
2167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // Populate task list from prefetch data...
2197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mTasks.ensureCapacity(totalTaskCount);
2207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int totalTaskIndex = 0;
2217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < viewCount; i++) {
2227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            RecyclerView view = mRecyclerViews.get(i);
2237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry;
2247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx)
2257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    + Math.abs(prefetchRegistry.mPrefetchDy);
2267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) {
2277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                final Task task;
2287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (totalTaskIndex >= mTasks.size()) {
2297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    task = new Task();
2307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mTasks.add(task);
2317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                } else {
2327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    task = mTasks.get(totalTaskIndex);
2337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
2347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1];
2357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                task.immediate = distanceToItem <= viewVelocity;
2377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                task.viewVelocity = viewVelocity;
2387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                task.distanceToItem = distanceToItem;
2397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                task.view = view;
2407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                task.position = prefetchRegistry.mPrefetchArray[j];
2417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                totalTaskIndex++;
2437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // ... and priority sort
2477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        Collections.sort(mTasks, sTaskComparator);
2487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
2497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static boolean isPrefetchPositionAttached(RecyclerView view, int position) {
2517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int childCount = view.mChildHelper.getUnfilteredChildCount();
2527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < childCount; i++) {
2537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            View attachedView = view.mChildHelper.getUnfilteredChildAt(i);
2547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView);
2557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // Note: can use mPosition here because adapter doesn't have pending updates
2567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (holder.mPosition == position && !holder.isInvalid()) {
2577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return true;
2587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return false;
2617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
2627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
2647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            int position, long deadlineNs) {
2657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (isPrefetchPositionAttached(view, position)) {
2667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // don't attempt to prefetch attached views
2677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return null;
2687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        RecyclerView.Recycler recycler = view.mRecycler;
2717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
2727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                position, false, deadlineNs);
2737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (holder != null) {
2757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (holder.isBound()) {
2767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // Only give the view a chance to go into the cache if binding succeeded
2777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // Note that we must use public method, since item may need cleanup
2787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                recycler.recycleView(holder.itemView);
2797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else {
2807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // Didn't bind, so we can't cache the view, but it will stay in the pool until
2817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // next prefetch/traversal. If a View fails to bind, it means we didn't have
2827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // enough time prior to the deadline (and won't for other instances of this
2837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // type, during this GapWorker prefetch pass).
2847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                recycler.addViewHolderToRecycledViewPool(holder, false);
2857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return holder;
2887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
2897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void prefetchInnerRecyclerViewWithDeadline(@Nullable RecyclerView innerView,
2917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            long deadlineNs) {
2927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (innerView == null) {
2937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return;
2947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (innerView.mDataSetHasChangedAfterLayout
2977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                && innerView.mChildHelper.getUnfilteredChildCount() != 0) {
2987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // RecyclerView has new data, but old attached views. Clear everything, so that
2997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // we can prefetch without partially stale data.
3007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            innerView.removeAndRecycleViews();
3017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // do nested prefetch!
3047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final LayoutPrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry;
3057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true);
3067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (innerPrefetchRegistry.mCount != 0) {
3087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            try {
3097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Trace.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG);
3107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                innerView.mState.prepareForNestedPrefetch(innerView.mAdapter);
3117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) {
3127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // Note that we ignore immediate flag for inner items because
3137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // we have lower confidence they're needed next frame.
3147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i];
3157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    prefetchPositionWithDeadline(innerView, innerPosition, deadlineNs);
3167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
3177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } finally {
3187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Trace.endSection();
3197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
3227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void flushTaskWithDeadline(Task task, long deadlineNs) {
3247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
3257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view,
3267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                task.position, taskDeadlineNs);
3277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (holder != null && holder.mNestedRecyclerView != null) {
3287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs);
3297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
3317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void flushTasksWithDeadline(long deadlineNs) {
3337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < mTasks.size(); i++) {
3347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            final Task task = mTasks.get(i);
3357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (task.view == null) {
3367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break; // done with populated tasks
3377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            flushTaskWithDeadline(task, deadlineNs);
3397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            task.clear();
3407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
3427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void prefetch(long deadlineNs) {
3447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        buildTaskList();
3457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        flushTasksWithDeadline(deadlineNs);
3467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
3477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    @Override
3497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    public void run() {
3507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        try {
3517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Trace.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
3527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mRecyclerViews.isEmpty()) {
3547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // abort - no work to do
3557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return;
3567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // Query last vsync so we can predict next one. Note that drawing time not yet
3597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // valid in animation/input callbacks, so query it here to be safe.
3607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos(
3617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mRecyclerViews.get(0).getDrawingTime());
3627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (lastFrameVsyncNs == 0) {
3637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // abort - couldn't get last vsync for estimating next
3647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return;
3657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // TODO: consider rebasing deadline if frame was already dropped due to long UI work.
3687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // Next frame will still wait for VSYNC, so we can still use the gap if it exists.
3697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs;
3707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            prefetch(nextFrameNs);
3727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            // TODO: consider rescheduling self, if there's more work to do
3747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        } finally {
3757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mPostTimeNs = 0;
3767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Trace.endSection();
3777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
3797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas}
380