1e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project
3e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar *
4e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * you may not use this file except in compliance with the License.
6e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * You may obtain a copy of the License at
7e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar *
8e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar *
10e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * See the License for the specific language governing permissions and
14e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * limitations under the License.
15e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */
16ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget;
17e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
18ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_AND_DISAPPEAR;
20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_PRE_AND_POST;
21ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
22ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_POST;
23ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
24ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_PRE_AND_POST;
251e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas
26ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull;
27ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable;
28ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.VisibleForTesting;
29ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.collection.ArrayMap;
30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.collection.LongSparseArray;
31ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.util.Pools;
32e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar/**
33c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette * This class abstracts all tracking for Views to run animations.
34e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */
35e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyarclass ViewInfoStore {
36e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
37e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    private static final boolean DEBUG = false;
38e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
39e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
40e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * View data records for pre-layout
41e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
42e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    @VisibleForTesting
43ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    final ArrayMap<RecyclerView.ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>();
44e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
45e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    @VisibleForTesting
46ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    final LongSparseArray<RecyclerView.ViewHolder> mOldChangedHolders = new LongSparseArray<>();
47e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
48e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
49e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Clears the state and all existing tracking data
50e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
51e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    void clear() {
52e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        mLayoutHolderMap.clear();
53e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        mOldChangedHolders.clear();
54e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
55e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
56e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
57e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Adds the item information to the prelayout tracking
58e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param holder The ViewHolder whose information is being saved
59e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param info The information to save
60e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
61ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    void addToPreLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) {
62e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        InfoRecord record = mLayoutHolderMap.get(holder);
63e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        if (record == null) {
64e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            record = InfoRecord.obtain();
65e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            mLayoutHolderMap.put(holder, record);
66e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
67e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.preInfo = info;
68e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.flags |= FLAG_PRE;
69e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
70e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
71ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    boolean isDisappearing(RecyclerView.ViewHolder holder) {
7213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        final InfoRecord record = mLayoutHolderMap.get(holder);
7313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        return record != null && ((record.flags & FLAG_DISAPPEARED) != 0);
7413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    }
7513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
76e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
77e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Finds the ItemHolderInfo for the given ViewHolder in preLayout list and removes it.
7813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar     *
79e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param vh The ViewHolder whose information is being queried
80e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
81e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
82e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    @Nullable
83ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    RecyclerView.ItemAnimator.ItemHolderInfo popFromPreLayout(RecyclerView.ViewHolder vh) {
8413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        return popFromLayoutStep(vh, FLAG_PRE);
8513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    }
8613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
8713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    /**
8813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar     * Finds the ItemHolderInfo for the given ViewHolder in postLayout list and removes it.
8913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar     *
9013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar     * @param vh The ViewHolder whose information is being queried
9113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar     * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
9213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar     */
9313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    @Nullable
94ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    RecyclerView.ItemAnimator.ItemHolderInfo popFromPostLayout(RecyclerView.ViewHolder vh) {
9513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        return popFromLayoutStep(vh, FLAG_POST);
9613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    }
9713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
98ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    private RecyclerView.ItemAnimator.ItemHolderInfo popFromLayoutStep(RecyclerView.ViewHolder vh, int flag) {
99e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        int index = mLayoutHolderMap.indexOfKey(vh);
100e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        if (index < 0) {
101e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            return null;
102e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
103e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        final InfoRecord record = mLayoutHolderMap.valueAt(index);
10413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        if (record != null && (record.flags & flag) != 0) {
10513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            record.flags &= ~flag;
106ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas            final RecyclerView.ItemAnimator.ItemHolderInfo info;
10713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            if (flag == FLAG_PRE) {
10813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                info = record.preInfo;
10913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            } else if (flag == FLAG_POST) {
11013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                info = record.postInfo;
11113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            } else {
11213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                throw new IllegalArgumentException("Must provide flag PRE or POST");
11313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            }
11413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            // if not pre-post flag is left, clear.
11513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            if ((record.flags & (FLAG_PRE | FLAG_POST)) == 0) {
116e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                mLayoutHolderMap.removeAt(index);
117e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                InfoRecord.recycle(record);
118e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            }
119e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            return info;
120e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
121e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        return null;
122e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
123e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
124e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
125e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Adds the given ViewHolder to the oldChangeHolders list
126e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param key The key to identify the ViewHolder.
127e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param holder The ViewHolder to store
128e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
129ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    void addToOldChangeHolders(long key, RecyclerView.ViewHolder holder) {
130e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        mOldChangedHolders.put(key, holder);
131e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
132e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
133e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
134e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Adds the given ViewHolder to the appeared in pre layout list. These are Views added by the
135e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * LayoutManager during a pre-layout pass. We distinguish them from other views that were
136e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * already in the pre-layout so that ItemAnimator can choose to run a different animation for
137e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * them.
138e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     *
139e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param holder The ViewHolder to store
140e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param info The information to save
141e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
142ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    void addToAppearedInPreLayoutHolders(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) {
143e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        InfoRecord record = mLayoutHolderMap.get(holder);
144e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        if (record == null) {
145e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            record = InfoRecord.obtain();
146e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            mLayoutHolderMap.put(holder, record);
147e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
148e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.flags |= FLAG_APPEAR;
149e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.preInfo = info;
150e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
151e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
152e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
153e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Checks whether the given ViewHolder is in preLayout list
154e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param viewHolder The ViewHolder to query
155e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     *
156e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @return True if the ViewHolder is present in preLayout, false otherwise
157e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
158ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    boolean isInPreLayout(RecyclerView.ViewHolder viewHolder) {
159e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        final InfoRecord record = mLayoutHolderMap.get(viewHolder);
160e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        return record != null && (record.flags & FLAG_PRE) != 0;
161e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
162e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
163e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
164e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Queries the oldChangeHolder list for the given key. If they are not tracked, simply returns
165e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * null.
166e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param key The key to be used to find the ViewHolder.
167e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     *
168e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @return A ViewHolder if exists or null if it does not exist.
169e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
170ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    RecyclerView.ViewHolder getFromOldChangeHolders(long key) {
171e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        return mOldChangedHolders.get(key);
172e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
173e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
174e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
175e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Adds the item information to the post layout list
176e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param holder The ViewHolder whose information is being saved
177e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param info The information to save
178e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
179ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    void addToPostLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) {
180e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        InfoRecord record = mLayoutHolderMap.get(holder);
181e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        if (record == null) {
182e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            record = InfoRecord.obtain();
183e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            mLayoutHolderMap.put(holder, record);
184e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
185e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.postInfo = info;
186e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.flags |= FLAG_POST;
187e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
188e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
189e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
190e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * A ViewHolder might be added by the LayoutManager just to animate its disappearance.
191e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * This list holds such items so that we can animate / recycle these ViewHolders properly.
192e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     *
193e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param holder The ViewHolder which disappeared during a layout.
194e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
195ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    void addToDisappearedInLayout(RecyclerView.ViewHolder holder) {
196e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        InfoRecord record = mLayoutHolderMap.get(holder);
197e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        if (record == null) {
198e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            record = InfoRecord.obtain();
199e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            mLayoutHolderMap.put(holder, record);
200e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
201e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.flags |= FLAG_DISAPPEARED;
202e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
203e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
204e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
205e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Removes a ViewHolder from disappearing list.
206e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param holder The ViewHolder to be removed from the disappearing list.
207e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
208ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    void removeFromDisappearedInLayout(RecyclerView.ViewHolder holder) {
209e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        InfoRecord record = mLayoutHolderMap.get(holder);
210e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        if (record == null) {
211e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            return;
212e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
213e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        record.flags &= ~FLAG_DISAPPEARED;
214e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
215e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
216e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    void process(ProcessCallback callback) {
2171e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas        for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
218ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas            final RecyclerView.ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
219e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            final InfoRecord record = mLayoutHolderMap.removeAt(index);
220e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
221e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                // Appeared then disappeared. Not useful for animations.
222e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                callback.unused(viewHolder);
223e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            } else if ((record.flags & FLAG_DISAPPEARED) != 0) {
224e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                // Set as "disappeared" by the LayoutManager (addDisappearingView)
225a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar                if (record.preInfo == null) {
226a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar                    // similar to appear disappear but happened between different layout passes.
227a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar                    // this can happen when the layout manager is using auto-measure
228a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar                    callback.unused(viewHolder);
229a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar                } else {
230a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar                    callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
231a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar                }
232e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            } else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) {
233e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                // Appeared in the layout but not in the adapter (e.g. entered the viewport)
234e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
235e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            } else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
236e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                // Persistent in both passes. Animate persistence
237e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
238e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            } else if ((record.flags & FLAG_PRE) != 0) {
239e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                // Was in pre-layout, never been added to post layout
240e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                callback.processDisappeared(viewHolder, record.preInfo, null);
241e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            } else if ((record.flags & FLAG_POST) != 0) {
242e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                // Was not in pre-layout, been added to post layout
243e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
244e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            } else if ((record.flags & FLAG_APPEAR) != 0) {
245e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                // Scrap view. RecyclerView will handle removing/recycling this.
246e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            } else if (DEBUG) {
247e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                throw new IllegalStateException("record without any reasonable flag combination:/");
248e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            }
249e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            InfoRecord.recycle(record);
250e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
251e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
252e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
253e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    /**
254e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * Removes the ViewHolder from all list
255e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     * @param holder The ViewHolder which we should stop tracking
256e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar     */
257ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    void removeViewHolder(RecyclerView.ViewHolder holder) {
258e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        for (int i = mOldChangedHolders.size() - 1; i >= 0; i--) {
259e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            if (holder == mOldChangedHolders.valueAt(i)) {
260e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                mOldChangedHolders.removeAt(i);
261e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                break;
262e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            }
263e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
264e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        final InfoRecord info = mLayoutHolderMap.remove(holder);
265e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        if (info != null) {
266e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            InfoRecord.recycle(info);
267e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
268e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
269e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
270e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    void onDetach() {
271e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        InfoRecord.drainCache();
272e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
273e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
274ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas    public void onViewDetached(RecyclerView.ViewHolder viewHolder) {
2754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        removeFromDisappearedInLayout(viewHolder);
2764143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
2774143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
278e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    interface ProcessCallback {
279ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        void processDisappeared(RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ItemAnimator.ItemHolderInfo preInfo,
280ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas                @Nullable RecyclerView.ItemAnimator.ItemHolderInfo postInfo);
281ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        void processAppeared(RecyclerView.ViewHolder viewHolder, @Nullable RecyclerView.ItemAnimator.ItemHolderInfo preInfo,
282ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas                RecyclerView.ItemAnimator.ItemHolderInfo postInfo);
283ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        void processPersistent(RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ItemAnimator.ItemHolderInfo preInfo,
284ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas                @NonNull RecyclerView.ItemAnimator.ItemHolderInfo postInfo);
285ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        void unused(RecyclerView.ViewHolder holder);
286e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
287e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
288e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    static class InfoRecord {
289e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        // disappearing list
290e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static final int FLAG_DISAPPEARED = 1;
291e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        // appear in pre layout list
292e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static final int FLAG_APPEAR = 1 << 1;
293e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        // pre layout, this is necessary to distinguish null item info
294e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static final int FLAG_PRE = 1 << 2;
295e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        // post layout, this is necessary to distinguish null item info
296e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static final int FLAG_POST = 1 << 3;
297e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static final int FLAG_APPEAR_AND_DISAPPEAR = FLAG_APPEAR | FLAG_DISAPPEARED;
298e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static final int FLAG_PRE_AND_POST = FLAG_PRE | FLAG_POST;
299e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static final int FLAG_APPEAR_PRE_AND_POST = FLAG_APPEAR | FLAG_PRE | FLAG_POST;
300e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        int flags;
301ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        @Nullable
302ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        RecyclerView.ItemAnimator.ItemHolderInfo preInfo;
303ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        @Nullable
304ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas        RecyclerView.ItemAnimator.ItemHolderInfo postInfo;
305e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static Pools.Pool<InfoRecord> sPool = new Pools.SimplePool<>(20);
306e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
307e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        private InfoRecord() {
308e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
309e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
310e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static InfoRecord obtain() {
311e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            InfoRecord record = sPool.acquire();
312e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            return record == null ? new InfoRecord() : record;
313e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
314e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
315e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static void recycle(InfoRecord record) {
316e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            record.flags = 0;
317e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            record.preInfo = null;
318e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            record.postInfo = null;
319e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            sPool.release(record);
320e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
321e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar
322e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        static void drainCache() {
323e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            //noinspection StatementWithEmptyBody
324e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            while (sPool.acquire() != null);
325e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        }
326e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar    }
327e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar}
328