18ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project
38ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar *
48ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
58ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * you may not use this file except in compliance with the License.
68ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * You may obtain a copy of the License at
78ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar *
88ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
98ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar *
108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Unless required by applicable law or agreed to in writing, software
118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * See the License for the specific language governing permissions and
148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * limitations under the License.
158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */
168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget;
188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
191faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyarimport android.util.Log;
208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
21c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.core.util.Pools;
22c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas
238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport java.util.ArrayList;
248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport java.util.Collections;
258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport java.util.List;
268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar/**
288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Helper class that can enqueue and process adapter update operations.
298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p>
308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * To support animations, RecyclerView presents an older version the Adapter to best represent
318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * previous state of the layout. Sometimes, this is not trivial when items are removed that were
328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * not laid out, in which case, RecyclerView has no way of providing that item's view for
338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * animations.
348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p>
358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During
368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass
378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them
388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * according to previously deferred operation and dispatch them before the first layout pass. It
398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * also takes care of updating deferred UpdateOps since order of operations is changed by this
408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * process.
418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p>
428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Although operations may be forwarded to LayoutManager in different orders, resulting data set
438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * is guaranteed to be the consistent.
448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */
454b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyarclass AdapterHelper implements OpReorderer.Callback {
468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
471e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas    static final int POSITION_TYPE_INVISIBLE = 0;
488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
491e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas    static final int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
511faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    private static final boolean DEBUG = false;
521faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
531faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    private static final String TAG = "AHT";
541faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    private Pools.Pool<UpdateOp> mUpdateOpPool = new Pools.SimplePool<UpdateOp>(UpdateOp.POOL_SIZE);
568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    final ArrayList<UpdateOp> mPostponedList = new ArrayList<UpdateOp>();
608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    final Callback mCallback;
628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    Runnable mOnItemProcessedCallback;
648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    final boolean mDisableRecycler;
668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
674b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    final OpReorderer mOpReorderer;
684b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar
69121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar    private int mExistingUpdateTypes = 0;
70121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar
718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    AdapterHelper(Callback callback) {
728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        this(callback, false);
738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    AdapterHelper(Callback callback, boolean disableRecycler) {
768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mCallback = callback;
778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mDisableRecycler = disableRecycler;
784b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        mOpReorderer = new OpReorderer(this);
798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    AdapterHelper addUpdateOp(UpdateOp... ops) {
828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        Collections.addAll(mPendingUpdates, ops);
838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return this;
848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    void reset() {
878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        recycleUpdateOpsAndClearList(mPendingUpdates);
888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        recycleUpdateOpsAndClearList(mPostponedList);
89121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        mExistingUpdateTypes = 0;
908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    void preProcess() {
934b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        mOpReorderer.reorderOps(mPendingUpdates);
948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final int count = mPendingUpdates.size();
958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < count; i++) {
968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            UpdateOp op = mPendingUpdates.get(i);
978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            switch (op.cmd) {
988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case UpdateOp.ADD:
998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    applyAdd(op);
1008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    break;
1018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case UpdateOp.REMOVE:
1028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    applyRemove(op);
1038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    break;
1048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case UpdateOp.UPDATE:
1058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    applyUpdate(op);
1068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    break;
1071faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                case UpdateOp.MOVE:
1081faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    applyMove(op);
1091faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    break;
1108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
1118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mOnItemProcessedCallback != null) {
1128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mOnItemProcessedCallback.run();
1138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
1148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mPendingUpdates.clear();
1168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
1178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    void consumePostponedUpdates() {
1198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final int count = mPostponedList.size();
1208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < count; i++) {
1218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mCallback.onDispatchSecondPass(mPostponedList.get(i));
1228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        recycleUpdateOpsAndClearList(mPostponedList);
124121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        mExistingUpdateTypes = 0;
1258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
1268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1271faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    private void applyMove(UpdateOp op) {
1281faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        // MOVE ops are pre-processed so at this point, we know that item is still in the adapter.
1291faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        // otherwise, it would be converted into a REMOVE operation
1304b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        postponeAndUpdateViewHolders(op);
1311faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    }
1321faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
1338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    private void applyRemove(UpdateOp op) {
1348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int tmpStart = op.positionStart;
1358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int tmpCount = 0;
1368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int tmpEnd = op.positionStart + op.itemCount;
1378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int type = -1;
1388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int position = op.positionStart; position < tmpEnd; position++) {
1398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            boolean typeChanged = false;
140a4b6d823ff4dc52ec665fc4b6d7ddde5e18e0560Yigit Boyar            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
1411faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (vh != null || canFindInPreLayout(position)) {
1428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // If a ViewHolder exists or this is a newly added item, we can defer this update
1438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // to post layout stage.
1448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // * For existing ViewHolders, we'll fake its existence in the pre-layout phase.
1458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // * For items that are added and removed in the same process cycle, they won't
1468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // have any effect in pre-layout since their add ops are already deferred to
1478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // post-layout pass.
1488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (type == POSITION_TYPE_INVISIBLE) {
1498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    // Looks like we have other updates that we cannot merge with this one.
1508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    // Create an UpdateOp and dispatch it to LayoutManager.
15121b345f101abc385496f42d250e580d21f1287b6Dake Gu                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
1524b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                    dispatchAndUpdateViewHolders(newOp);
1538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    typeChanged = true;
1548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
1558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                type = POSITION_TYPE_NEW_OR_LAID_OUT;
1568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
1578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // This update cannot be recovered because we don't have a ViewHolder representing
1588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // this position. Instead, post it to LayoutManager immediately
1598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
1608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    // Looks like we have other updates that we cannot merge with this one.
1618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    // Create UpdateOp op and dispatch it to LayoutManager.
16221b345f101abc385496f42d250e580d21f1287b6Dake Gu                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
1634b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                    postponeAndUpdateViewHolders(newOp);
1648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    typeChanged = true;
1658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
1668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                type = POSITION_TYPE_INVISIBLE;
1678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
1688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (typeChanged) {
1698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                position -= tmpCount; // also equal to tmpStart
1708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                tmpEnd -= tmpCount;
1718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                tmpCount = 1;
1728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
1738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                tmpCount++;
1748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
1758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (tmpCount != op.itemCount) { // all 1 effect
1778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            recycleUpdateOp(op);
17821b345f101abc385496f42d250e580d21f1287b6Dake Gu            op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
1798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (type == POSITION_TYPE_INVISIBLE) {
1814b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            dispatchAndUpdateViewHolders(op);
1828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        } else {
1834b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            postponeAndUpdateViewHolders(op);
1848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
1868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    private void applyUpdate(UpdateOp op) {
1888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int tmpStart = op.positionStart;
1898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int tmpCount = 0;
1908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int tmpEnd = op.positionStart + op.itemCount;
1918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int type = -1;
1928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int position = op.positionStart; position < tmpEnd; position++) {
193a4b6d823ff4dc52ec665fc4b6d7ddde5e18e0560Yigit Boyar            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
1941faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (vh != null || canFindInPreLayout(position)) { // deferred
1958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (type == POSITION_TYPE_INVISIBLE) {
19621b345f101abc385496f42d250e580d21f1287b6Dake Gu                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
19721b345f101abc385496f42d250e580d21f1287b6Dake Gu                            op.payload);
1984b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                    dispatchAndUpdateViewHolders(newOp);
1998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    tmpCount = 0;
200e4fde6825bba479c9b030feb8f810694d46b2f06Yigit Boyar                    tmpStart = position;
2018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
2028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                type = POSITION_TYPE_NEW_OR_LAID_OUT;
2038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else { // applied
2048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
20521b345f101abc385496f42d250e580d21f1287b6Dake Gu                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
20621b345f101abc385496f42d250e580d21f1287b6Dake Gu                            op.payload);
2074b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                    postponeAndUpdateViewHolders(newOp);
2088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    tmpCount = 0;
209e4fde6825bba479c9b030feb8f810694d46b2f06Yigit Boyar                    tmpStart = position;
2108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
2118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                type = POSITION_TYPE_INVISIBLE;
2128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
2138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            tmpCount++;
2148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
2158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (tmpCount != op.itemCount) { // all 1 effect
21621b345f101abc385496f42d250e580d21f1287b6Dake Gu            Object payload = op.payload;
2178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            recycleUpdateOp(op);
21821b345f101abc385496f42d250e580d21f1287b6Dake Gu            op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount, payload);
2198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
2208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (type == POSITION_TYPE_INVISIBLE) {
2214b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            dispatchAndUpdateViewHolders(op);
2228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        } else {
2234b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            postponeAndUpdateViewHolders(op);
2248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
2258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
2268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
2274b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    private void dispatchAndUpdateViewHolders(UpdateOp op) {
2288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // tricky part.
2298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // traverse all postpones and revert their changes on this op if necessary, apply updated
2308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // dispatch to them since now they are after this op.
2311faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) {
2321faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            throw new IllegalArgumentException("should not dispatch add or move for pre layout");
2331faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
2341faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (DEBUG) {
2354b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            Log.d(TAG, "dispatch (pre)" + op);
2361faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            Log.d(TAG, "postponed state before:");
2371faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            for (UpdateOp updateOp : mPostponedList) {
2381faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                Log.d(TAG, updateOp.toString());
2391faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
2401faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            Log.d(TAG, "----");
2411faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
2421faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
2431faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        // handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial
2444b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        // TODO Since move ops are pushed to end, we should not need this anymore
2451faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd);
2461faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (DEBUG) {
2471faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart);
2481faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
2491faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        int tmpCnt = 1;
2504b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        int offsetPositionForPartial = op.positionStart;
2514b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        final int positionMultiplier;
2524b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        switch (op.cmd) {
2534b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.UPDATE:
2544b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                positionMultiplier = 1;
2554b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
2564b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.REMOVE:
2574b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                positionMultiplier = 0;
2584b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
2594b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            default:
2604b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                throw new IllegalArgumentException("op should be remove or update." + op);
2614b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        }
2621faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        for (int p = 1; p < op.itemCount; p++) {
2634b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            final int pos = op.positionStart + (positionMultiplier * p);
2641faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            int updatedPos = updatePositionWithPostponed(pos, op.cmd);
2651faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (DEBUG) {
2661faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos);
2671faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
2681faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            boolean continuous = false;
2691faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            switch (op.cmd) {
2701faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                case UpdateOp.UPDATE:
2711faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    continuous = updatedPos == tmpStart + 1;
2721faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    break;
2731faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                case UpdateOp.REMOVE:
2741faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    continuous = updatedPos == tmpStart;
2751faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    break;
2761faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
2771faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (continuous) {
2781faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                tmpCnt++;
2791faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            } else {
2801faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                // need to dispatch this separately
28121b345f101abc385496f42d250e580d21f1287b6Dake Gu                UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, op.payload);
2821faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                if (DEBUG) {
2831faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    Log.d(TAG, "need to dispatch separately " + tmp);
2841faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
2854b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
2861faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                recycleUpdateOp(tmp);
2874b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                if (op.cmd == UpdateOp.UPDATE) {
2884b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                    offsetPositionForPartial += tmpCnt;
2894b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                }
2901e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                tmpStart = updatedPos; // need to remove previously dispatched
2911faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                tmpCnt = 1;
2921faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
2931faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
29421b345f101abc385496f42d250e580d21f1287b6Dake Gu        Object payload = op.payload;
2951faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        recycleUpdateOp(op);
2961faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (tmpCnt > 0) {
29721b345f101abc385496f42d250e580d21f1287b6Dake Gu            UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, payload);
2981faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (DEBUG) {
2991faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                Log.d(TAG, "dispatching:" + tmp);
3001faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
3014b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
3021faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            recycleUpdateOp(tmp);
3031faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
3041faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (DEBUG) {
3051faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            Log.d(TAG, "post dispatch");
3061faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            Log.d(TAG, "postponed state after:");
3071faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            for (UpdateOp updateOp : mPostponedList) {
3081faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                Log.d(TAG, updateOp.toString());
3091faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
3101faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            Log.d(TAG, "----");
3111faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
3121faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    }
3131faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
3144b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) {
3154b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        mCallback.onDispatchFirstPass(op);
3164b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        switch (op.cmd) {
3174b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.REMOVE:
3184b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount);
3194b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
3204b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.UPDATE:
32121b345f101abc385496f42d250e580d21f1287b6Dake Gu                mCallback.markViewHoldersUpdated(offsetStart, op.itemCount, op.payload);
3224b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
3234b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            default:
3244b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                throw new IllegalArgumentException("only remove and update ops can be dispatched"
3254b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                        + " in first pass");
3264b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        }
3274b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    }
3284b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar
3291faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    private int updatePositionWithPostponed(int pos, int cmd) {
3308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final int count = mPostponedList.size();
3318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = count - 1; i >= 0; i--) {
3328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            UpdateOp postponed = mPostponedList.get(i);
3331faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (postponed.cmd == UpdateOp.MOVE) {
3341faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                int start, end;
3351faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                if (postponed.positionStart < postponed.itemCount) {
3361faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    start = postponed.positionStart;
3371faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    end = postponed.itemCount;
3381faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                } else {
3391faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    start = postponed.itemCount;
3401faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    end = postponed.positionStart;
3411faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
3421faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                if (pos >= start && pos <= end) {
3431faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    //i'm affected
3441faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    if (start == postponed.positionStart) {
3451faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        if (cmd == UpdateOp.ADD) {
3461faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                            postponed.itemCount++;
3471faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        } else if (cmd == UpdateOp.REMOVE) {
3481faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                            postponed.itemCount--;
3491faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        }
3501faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        // op moved to left, move it right to revert
3511faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        pos++;
3521faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    } else {
3531faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        if (cmd == UpdateOp.ADD) {
3541faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                            postponed.positionStart++;
3551faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        } else if (cmd == UpdateOp.REMOVE) {
3561faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                            postponed.positionStart--;
3571faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        }
3581faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        // op was moved right, move left to revert
3591faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        pos--;
3601faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    }
3611faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                } else if (pos < postponed.positionStart) {
3621faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    // postponed MV is outside the dispatched OP. if it is before, offset
3631faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    if (cmd == UpdateOp.ADD) {
3641faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        postponed.positionStart++;
3651faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        postponed.itemCount++;
3661faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    } else if (cmd == UpdateOp.REMOVE) {
3671faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        postponed.positionStart--;
3681faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        postponed.itemCount--;
3691faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    }
3701faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
3718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
3721faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                if (postponed.positionStart <= pos) {
3731faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    if (postponed.cmd == UpdateOp.ADD) {
3741faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        pos -= postponed.itemCount;
3751faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    } else if (postponed.cmd == UpdateOp.REMOVE) {
3761faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        pos += postponed.itemCount;
3771faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    }
3781faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                } else {
3791faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    if (cmd == UpdateOp.ADD) {
3801faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        postponed.positionStart++;
3811faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    } else if (cmd == UpdateOp.REMOVE) {
3821faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        postponed.positionStart--;
3831faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    }
3841faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
3851faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
3861faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (DEBUG) {
3871faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                Log.d(TAG, "dispath (step" + i + ")");
3881faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                Log.d(TAG, "postponed state:" + i + ", pos:" + pos);
3891faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                for (UpdateOp updateOp : mPostponedList) {
3901faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    Log.d(TAG, updateOp.toString());
3911faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
3921faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                Log.d(TAG, "----");
3938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
3948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
3951faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        for (int i = mPostponedList.size() - 1; i >= 0; i--) {
3961faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            UpdateOp op = mPostponedList.get(i);
3971faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (op.cmd == UpdateOp.MOVE) {
3981faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                if (op.itemCount == op.positionStart || op.itemCount < 0) {
3991faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    mPostponedList.remove(i);
4001faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    recycleUpdateOp(op);
4011faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
4021faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            } else if (op.itemCount <= 0) {
4031faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                mPostponedList.remove(i);
4041faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                recycleUpdateOp(op);
4051faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            }
4061faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
4071faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        return pos;
4088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
4101faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    private boolean canFindInPreLayout(int position) {
4118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final int count = mPostponedList.size();
4128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < count; i++) {
4138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            UpdateOp op = mPostponedList.get(i);
4141faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (op.cmd == UpdateOp.MOVE) {
4154b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                if (findPositionOffset(op.itemCount, i + 1) == position) {
4161faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    return true;
4171faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
4181faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            } else if (op.cmd == UpdateOp.ADD) {
4191faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                // TODO optimize.
4201faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                final int end = op.positionStart + op.itemCount;
4211faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                for (int pos = op.positionStart; pos < end; pos++) {
4221faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    if (findPositionOffset(pos, i + 1) == position) {
4231faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                        return true;
4241faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    }
4251faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
4268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
4278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
4288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return false;
4298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
4318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    private void applyAdd(UpdateOp op) {
4324b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        postponeAndUpdateViewHolders(op);
4338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
4354b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    private void postponeAndUpdateViewHolders(UpdateOp op) {
4361faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (DEBUG) {
4371faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            Log.d(TAG, "postponing " + op);
4381faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
4398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mPostponedList.add(op);
4404b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        switch (op.cmd) {
4414b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.ADD:
4424b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
4434b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
4444b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.MOVE:
4454b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
4464b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
4474b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.REMOVE:
4484b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
4494b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                        op.itemCount);
4504b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
4514b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            case UpdateOp.UPDATE:
45221b345f101abc385496f42d250e580d21f1287b6Dake Gu                mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
4534b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                break;
4544b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            default:
4554b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                throw new IllegalArgumentException("Unknown update op type for " + op);
4564b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar        }
4578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
4598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    boolean hasPendingUpdates() {
4608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return mPendingUpdates.size() > 0;
4618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
463121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar    boolean hasAnyUpdateTypes(int updateTypes) {
464121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        return (mExistingUpdateTypes & updateTypes) != 0;
465121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar    }
466121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar
4678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    int findPositionOffset(int position) {
4688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return findPositionOffset(position, 0);
4698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
471668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    int findPositionOffset(int position, int firstPostponedItem) {
4728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int count = mPostponedList.size();
473668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (int i = firstPostponedItem; i < count; ++i) {
4748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            UpdateOp op = mPostponedList.get(i);
4751faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            if (op.cmd == UpdateOp.MOVE) {
4761faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                if (op.positionStart == position) {
4771faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    position = op.itemCount;
4784bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                } else {
4794bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                    if (op.positionStart < position) {
4804bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                        position--; // like a remove
4814bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                    }
4824bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                    if (op.itemCount <= position) {
4834bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                        position++; // like an add
4844bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                    }
4851faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                }
4861faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            } else if (op.positionStart <= position) {
4878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (op.cmd == UpdateOp.REMOVE) {
4884bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                    if (position < op.positionStart + op.itemCount) {
4894bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                        return -1;
4904bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar                    }
4911faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    position -= op.itemCount;
4928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                } else if (op.cmd == UpdateOp.ADD) {
4931faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    position += op.itemCount;
4948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
4958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
4968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
4971faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        return position;
4988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    /**
5018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     * @return True if updates should be processed.
5028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     */
50321b345f101abc385496f42d250e580d21f1287b6Dake Gu    boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5048fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        if (itemCount < 1) {
5058fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            return false;
5068fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        }
50721b345f101abc385496f42d250e580d21f1287b6Dake Gu        mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
508121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        mExistingUpdateTypes |= UpdateOp.UPDATE;
5098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return mPendingUpdates.size() == 1;
5108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    /**
5138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     * @return True if updates should be processed.
5148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     */
5158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    boolean onItemRangeInserted(int positionStart, int itemCount) {
5168fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        if (itemCount < 1) {
5178fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            return false;
5188fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        }
51921b345f101abc385496f42d250e580d21f1287b6Dake Gu        mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount, null));
520121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        mExistingUpdateTypes |= UpdateOp.ADD;
5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return mPendingUpdates.size() == 1;
5228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    /**
5258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     * @return True if updates should be processed.
5268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     */
5278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    boolean onItemRangeRemoved(int positionStart, int itemCount) {
5288fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        if (itemCount < 1) {
5298fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            return false;
5308fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        }
53121b345f101abc385496f42d250e580d21f1287b6Dake Gu        mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
532121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        mExistingUpdateTypes |= UpdateOp.REMOVE;
5338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return mPendingUpdates.size() == 1;
5348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    /**
5371faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar     * @return True if updates should be processed.
5381faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar     */
5391faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    boolean onItemRangeMoved(int from, int to, int itemCount) {
5401faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (from == to) {
5414143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            return false; // no-op
5421faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
5431faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        if (itemCount != 1) {
5441faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar            throw new IllegalArgumentException("Moving more than 1 item is not supported yet");
5451faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        }
54621b345f101abc385496f42d250e580d21f1287b6Dake Gu        mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to, null));
547121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        mExistingUpdateTypes |= UpdateOp.MOVE;
5481faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        return mPendingUpdates.size() == 1;
5491faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    }
5501faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
5511faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar    /**
5528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     * Skips pre-processing and applies all updates in one pass.
5538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     */
5548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    void consumeUpdatesInOnePass() {
5558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // we still consume postponed updates (if there is) in case there was a pre-process call
5568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // w/o a matching consumePostponedUpdates.
5578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        consumePostponedUpdates();
5588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final int count = mPendingUpdates.size();
5598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < count; i++) {
5608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            UpdateOp op = mPendingUpdates.get(i);
5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            switch (op.cmd) {
5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case UpdateOp.ADD:
5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    mCallback.onDispatchSecondPass(op);
56416e5adf5416248d97f2aeaa9d5e17bb5093130ebYigit Boyar                    mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
5658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    break;
5668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case UpdateOp.REMOVE:
5678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    mCallback.onDispatchSecondPass(op);
56816e5adf5416248d97f2aeaa9d5e17bb5093130ebYigit Boyar                    mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount);
5698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    break;
5708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case UpdateOp.UPDATE:
57116e5adf5416248d97f2aeaa9d5e17bb5093130ebYigit Boyar                    mCallback.onDispatchSecondPass(op);
57221b345f101abc385496f42d250e580d21f1287b6Dake Gu                    mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
5738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    break;
5741faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                case UpdateOp.MOVE:
57516e5adf5416248d97f2aeaa9d5e17bb5093130ebYigit Boyar                    mCallback.onDispatchSecondPass(op);
5761faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
5771faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    break;
5788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
5798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mOnItemProcessedCallback != null) {
5808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mOnItemProcessedCallback.run();
5818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
5828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
5838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        recycleUpdateOpsAndClearList(mPendingUpdates);
584121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        mExistingUpdateTypes = 0;
5858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
587115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    public int applyPendingUpdatesToPosition(int position) {
588115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final int size = mPendingUpdates.size();
5891e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas        for (int i = 0; i < size; i++) {
590115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            UpdateOp op = mPendingUpdates.get(i);
591115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            switch (op.cmd) {
592115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                case UpdateOp.ADD:
593115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    if (op.positionStart <= position) {
594115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        position += op.itemCount;
595115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    }
596115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    break;
597115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                case UpdateOp.REMOVE:
598115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    if (op.positionStart <= position) {
599115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        final int end = op.positionStart + op.itemCount;
600115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        if (end > position) {
601115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            return RecyclerView.NO_POSITION;
602115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        }
603115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        position -= op.itemCount;
604115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    }
605115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    break;
606115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                case UpdateOp.MOVE:
607115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    if (op.positionStart == position) {
6081e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                        position = op.itemCount; //position end
609115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    } else {
610115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        if (op.positionStart < position) {
611115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            position -= 1;
612115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        }
613115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        if (op.itemCount <= position) {
614115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            position += 1;
615115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        }
616115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    }
617115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    break;
618115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
619115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        }
620115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        return position;
621115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
622115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
6234143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    boolean hasUpdates() {
6244143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        return !mPostponedList.isEmpty() && !mPendingUpdates.isEmpty();
6254143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
6264143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
6278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    /**
6288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     * Queued operation to happen when child views are updated.
6298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     */
6308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    static class UpdateOp {
6318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
632121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        static final int ADD = 1;
6338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
634121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        static final int REMOVE = 1 << 1;
6358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
636121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        static final int UPDATE = 1 << 2;
6378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
638121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        static final int MOVE = 1 << 3;
6391faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
6408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        static final int POOL_SIZE = 30;
6418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int cmd;
6438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int positionStart;
6458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
64621b345f101abc385496f42d250e580d21f1287b6Dake Gu        Object payload;
64721b345f101abc385496f42d250e580d21f1287b6Dake Gu
6481faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        // holds the target position if this is a MOVE
6498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int itemCount;
6508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
65121b345f101abc385496f42d250e580d21f1287b6Dake Gu        UpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
6528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            this.cmd = cmd;
6538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            this.positionStart = positionStart;
6548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            this.itemCount = itemCount;
65521b345f101abc385496f42d250e580d21f1287b6Dake Gu            this.payload = payload;
6568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
6578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        String cmdToString() {
6598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            switch (cmd) {
6608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case ADD:
6618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    return "add";
6628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case REMOVE:
6638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    return "rm";
6648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                case UPDATE:
6658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    return "up";
6661faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                case MOVE:
6671faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar                    return "mv";
6688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return "??";
6708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
6718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
6738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public String toString() {
67421b345f101abc385496f42d250e580d21f1287b6Dake Gu            return Integer.toHexString(System.identityHashCode(this))
67521b345f101abc385496f42d250e580d21f1287b6Dake Gu                    + "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount
6761e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas                    + ",p:" + payload + "]";
6778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
6788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
6808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public boolean equals(Object o) {
6818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (this == o) {
6828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                return true;
6838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (o == null || getClass() != o.getClass()) {
6858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                return false;
6868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            UpdateOp op = (UpdateOp) o;
6898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (cmd != op.cmd) {
6918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                return false;
6928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6934b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) {
6944b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                // reverse of this is also true
6954b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                if (itemCount == op.positionStart && positionStart == op.itemCount) {
6964b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                    return true;
6974b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar                }
6984b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar            }
6998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (itemCount != op.itemCount) {
7008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                return false;
7018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (positionStart != op.positionStart) {
7038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                return false;
7048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
70521b345f101abc385496f42d250e580d21f1287b6Dake Gu            if (payload != null) {
70621b345f101abc385496f42d250e580d21f1287b6Dake Gu                if (!payload.equals(op.payload)) {
70721b345f101abc385496f42d250e580d21f1287b6Dake Gu                    return false;
70821b345f101abc385496f42d250e580d21f1287b6Dake Gu                }
70921b345f101abc385496f42d250e580d21f1287b6Dake Gu            } else if (op.payload != null) {
71021b345f101abc385496f42d250e580d21f1287b6Dake Gu                return false;
71121b345f101abc385496f42d250e580d21f1287b6Dake Gu            }
7128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return true;
7148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
7178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public int hashCode() {
7188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            int result = cmd;
7198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            result = 31 * result + positionStart;
7208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            result = 31 * result + itemCount;
7218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return result;
7228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7254b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    @Override
72621b345f101abc385496f42d250e580d21f1287b6Dake Gu    public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
7278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        UpdateOp op = mUpdateOpPool.acquire();
7288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (op == null) {
72921b345f101abc385496f42d250e580d21f1287b6Dake Gu            op = new UpdateOp(cmd, positionStart, itemCount, payload);
7308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        } else {
7318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            op.cmd = cmd;
7328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            op.positionStart = positionStart;
7338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            op.itemCount = itemCount;
73421b345f101abc385496f42d250e580d21f1287b6Dake Gu            op.payload = payload;
7358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return op;
7378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7394b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    @Override
7404b9b4d3fca81486051bac9aadb73d8865948c3bfYigit Boyar    public void recycleUpdateOp(UpdateOp op) {
7418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (!mDisableRecycler) {
74221b345f101abc385496f42d250e580d21f1287b6Dake Gu            op.payload = null;
7438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mUpdateOpPool.release(op);
7448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    void recycleUpdateOpsAndClearList(List<UpdateOp> ops) {
7488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final int count = ops.size();
7498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < count; i++) {
7508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            recycleUpdateOp(ops.get(i));
7518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        ops.clear();
7538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    /**
7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     * Contract between AdapterHelper and RecyclerView.
7578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar     */
758a4b6d823ff4dc52ec665fc4b6d7ddde5e18e0560Yigit Boyar    interface Callback {
7598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
760a4b6d823ff4dc52ec665fc4b6d7ddde5e18e0560Yigit Boyar        RecyclerView.ViewHolder findViewHolder(int position);
7618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void offsetPositionsForRemovingInvisible(int positionStart, int itemCount);
7638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount);
7658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
76621b345f101abc385496f42d250e580d21f1287b6Dake Gu        void markViewHoldersUpdated(int positionStart, int itemCount, Object payloads);
7678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void onDispatchFirstPass(UpdateOp updateOp);
7698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void onDispatchSecondPass(UpdateOp updateOp);
7718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void offsetPositionsForAdd(int positionStart, int itemCount);
7731faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar
7741faed0c7c20fc3a0b7befabbac1beac1019effc7Yigit Boyar        void offsetPositionsForMove(int from, int to);
7758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7764bacf13ec17763ba2e2d049d32a4a1dcca77a433Yigit Boyar}
777