17149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas/*
27149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Copyright (C) 2017 The Android Open Source Project
37149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *
47149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Licensed under the Apache License, Version 2.0 (the "License");
57149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * you may not use this file except in compliance with the License.
67149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * You may obtain a copy of the License at
77149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *
87149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *      http://www.apache.org/licenses/LICENSE-2.0
97149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas *
107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Unless required by applicable law or agreed to in writing, software
117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * distributed under the License is distributed on an "AS IS" BASIS,
127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * See the License for the specific language governing permissions and
147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * limitations under the License.
157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */
167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikaspackage com.android.internal.widget;
187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.util.Log;
207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.util.Pools;
217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.ArrayList;
237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.Collections;
247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.List;
257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas/**
277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Helper class that can enqueue and process adapter update operations.
287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>
297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * To support animations, RecyclerView presents an older version the Adapter to best represent
307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * previous state of the layout. Sometimes, this is not trivial when items are removed that were
317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * not laid out, in which case, RecyclerView has no way of providing that item's view for
327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * animations.
337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>
347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During
357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass
367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them
377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * according to previously deferred operation and dispatch them before the first layout pass. It
387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * also takes care of updating deferred UpdateOps since order of operations is changed by this
397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * process.
407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * <p>
417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Although operations may be forwarded to LayoutManager in different orders, resulting data set
427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * is guaranteed to be the consistent.
437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */
447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasclass AdapterHelper implements OpReorderer.Callback {
457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static final int POSITION_TYPE_INVISIBLE = 0;
477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static final int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private static final boolean DEBUG = false;
517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private static final String TAG = "AHT";
537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private Pools.Pool<UpdateOp> mUpdateOpPool = new Pools.SimplePool<UpdateOp>(UpdateOp.POOL_SIZE);
557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    final ArrayList<UpdateOp> mPostponedList = new ArrayList<UpdateOp>();
597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    final Callback mCallback;
617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    Runnable mOnItemProcessedCallback;
637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    final boolean mDisableRecycler;
657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    final OpReorderer mOpReorderer;
677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private int mExistingUpdateTypes = 0;
697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    AdapterHelper(Callback callback) {
717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        this(callback, false);
727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    AdapterHelper(Callback callback, boolean disableRecycler) {
757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mCallback = callback;
767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mDisableRecycler = disableRecycler;
777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mOpReorderer = new OpReorderer(this);
787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    AdapterHelper addUpdateOp(UpdateOp... ops) {
817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        Collections.addAll(mPendingUpdates, ops);
827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return this;
837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void reset() {
867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        recycleUpdateOpsAndClearList(mPendingUpdates);
877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        recycleUpdateOpsAndClearList(mPostponedList);
887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mExistingUpdateTypes = 0;
897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void preProcess() {
927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mOpReorderer.reorderOps(mPendingUpdates);
937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int count = mPendingUpdates.size();
947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < count; i++) {
957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp op = mPendingUpdates.get(i);
967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            switch (op.cmd) {
977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.ADD:
987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    applyAdd(op);
997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
1007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.REMOVE:
1017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    applyRemove(op);
1027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
1037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.UPDATE:
1047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    applyUpdate(op);
1057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
1067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.MOVE:
1077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    applyMove(op);
1087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
1097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mOnItemProcessedCallback != null) {
1117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mOnItemProcessedCallback.run();
1127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mPendingUpdates.clear();
1157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void consumePostponedUpdates() {
1187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int count = mPostponedList.size();
1197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < count; i++) {
1207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mCallback.onDispatchSecondPass(mPostponedList.get(i));
1217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        recycleUpdateOpsAndClearList(mPostponedList);
1237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mExistingUpdateTypes = 0;
1247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void applyMove(UpdateOp op) {
1277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // MOVE ops are pre-processed so at this point, we know that item is still in the adapter.
1287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // otherwise, it would be converted into a REMOVE operation
1297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        postponeAndUpdateViewHolders(op);
1307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void applyRemove(UpdateOp op) {
1337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpStart = op.positionStart;
1347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpCount = 0;
1357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpEnd = op.positionStart + op.itemCount;
1367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int type = -1;
1377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int position = op.positionStart; position < tmpEnd; position++) {
1387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            boolean typeChanged = false;
1397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
1407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (vh != null || canFindInPreLayout(position)) {
1417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // If a ViewHolder exists or this is a newly added item, we can defer this update
1427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // to post layout stage.
1437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // * For existing ViewHolders, we'll fake its existence in the pre-layout phase.
1447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // * For items that are added and removed in the same process cycle, they won't
1457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // have any effect in pre-layout since their add ops are already deferred to
1467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // post-layout pass.
1477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (type == POSITION_TYPE_INVISIBLE) {
1487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // Looks like we have other updates that we cannot merge with this one.
1497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // Create an UpdateOp and dispatch it to LayoutManager.
1507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
1517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    dispatchAndUpdateViewHolders(newOp);
1527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    typeChanged = true;
1537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
1547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                type = POSITION_TYPE_NEW_OR_LAID_OUT;
1557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else {
1567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // This update cannot be recovered because we don't have a ViewHolder representing
1577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // this position. Instead, post it to LayoutManager immediately
1587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
1597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // Looks like we have other updates that we cannot merge with this one.
1607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // Create UpdateOp op and dispatch it to LayoutManager.
1617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
1627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    postponeAndUpdateViewHolders(newOp);
1637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    typeChanged = true;
1647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
1657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                type = POSITION_TYPE_INVISIBLE;
1667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (typeChanged) {
1687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                position -= tmpCount; // also equal to tmpStart
1697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                tmpEnd -= tmpCount;
1707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                tmpCount = 1;
1717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else {
1727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                tmpCount++;
1737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
1747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (tmpCount != op.itemCount) { // all 1 effect
1767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            recycleUpdateOp(op);
1777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
1787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (type == POSITION_TYPE_INVISIBLE) {
1807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            dispatchAndUpdateViewHolders(op);
1817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        } else {
1827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            postponeAndUpdateViewHolders(op);
1837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
1847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
1857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
1867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void applyUpdate(UpdateOp op) {
1877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpStart = op.positionStart;
1887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpCount = 0;
1897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpEnd = op.positionStart + op.itemCount;
1907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int type = -1;
1917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int position = op.positionStart; position < tmpEnd; position++) {
1927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
1937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (vh != null || canFindInPreLayout(position)) { // deferred
1947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (type == POSITION_TYPE_INVISIBLE) {
1957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
1967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            op.payload);
1977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    dispatchAndUpdateViewHolders(newOp);
1987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    tmpCount = 0;
1997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    tmpStart = position;
2007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
2017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                type = POSITION_TYPE_NEW_OR_LAID_OUT;
2027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else { // applied
2037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
2047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
2057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            op.payload);
2067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    postponeAndUpdateViewHolders(newOp);
2077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    tmpCount = 0;
2087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    tmpStart = position;
2097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
2107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                type = POSITION_TYPE_INVISIBLE;
2117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            tmpCount++;
2137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (tmpCount != op.itemCount) { // all 1 effect
2157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Object payload = op.payload;
2167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            recycleUpdateOp(op);
2177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount, payload);
2187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (type == POSITION_TYPE_INVISIBLE) {
2207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            dispatchAndUpdateViewHolders(op);
2217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        } else {
2227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            postponeAndUpdateViewHolders(op);
2237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
2257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void dispatchAndUpdateViewHolders(UpdateOp op) {
2277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // tricky part.
2287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // traverse all postpones and revert their changes on this op if necessary, apply updated
2297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // dispatch to them since now they are after this op.
2307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) {
2317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            throw new IllegalArgumentException("should not dispatch add or move for pre layout");
2327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (DEBUG) {
2347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "dispatch (pre)" + op);
2357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "postponed state before:");
2367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            for (UpdateOp updateOp : mPostponedList) {
2377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Log.d(TAG, updateOp.toString());
2387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "----");
2407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
2427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial
2437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // TODO Since move ops are pushed to end, we should not need this anymore
2447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd);
2457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (DEBUG) {
2467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart);
2477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int tmpCnt = 1;
2497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int offsetPositionForPartial = op.positionStart;
2507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int positionMultiplier;
2517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        switch (op.cmd) {
2527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.UPDATE:
2537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                positionMultiplier = 1;
2547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
2557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.REMOVE:
2567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                positionMultiplier = 0;
2577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
2587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            default:
2597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                throw new IllegalArgumentException("op should be remove or update." + op);
2607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int p = 1; p < op.itemCount; p++) {
2627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            final int pos = op.positionStart + (positionMultiplier * p);
2637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            int updatedPos = updatePositionWithPostponed(pos, op.cmd);
2647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (DEBUG) {
2657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos);
2667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            boolean continuous = false;
2687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            switch (op.cmd) {
2697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.UPDATE:
2707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    continuous = updatedPos == tmpStart + 1;
2717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
2727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.REMOVE:
2737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    continuous = updatedPos == tmpStart;
2747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
2757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (continuous) {
2777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                tmpCnt++;
2787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else {
2797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // need to dispatch this separately
2807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, op.payload);
2817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (DEBUG) {
2827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    Log.d(TAG, "need to dispatch separately " + tmp);
2837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
2847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
2857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                recycleUpdateOp(tmp);
2867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (op.cmd == UpdateOp.UPDATE) {
2877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    offsetPositionForPartial += tmpCnt;
2887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
2897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                tmpStart = updatedPos; // need to remove previously dispatched
2907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                tmpCnt = 1;
2917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
2927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
2937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        Object payload = op.payload;
2947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        recycleUpdateOp(op);
2957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (tmpCnt > 0) {
2967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, payload);
2977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (DEBUG) {
2987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Log.d(TAG, "dispatching:" + tmp);
2997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
3017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            recycleUpdateOp(tmp);
3027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (DEBUG) {
3047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "post dispatch");
3057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "postponed state after:");
3067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            for (UpdateOp updateOp : mPostponedList) {
3077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Log.d(TAG, updateOp.toString());
3087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "----");
3107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
3127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) {
3147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mCallback.onDispatchFirstPass(op);
3157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        switch (op.cmd) {
3167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.REMOVE:
3177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount);
3187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
3197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.UPDATE:
3207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mCallback.markViewHoldersUpdated(offsetStart, op.itemCount, op.payload);
3217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
3227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            default:
3237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                throw new IllegalArgumentException("only remove and update ops can be dispatched"
3247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        + " in first pass");
3257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
3277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
3287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private int updatePositionWithPostponed(int pos, int cmd) {
3297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int count = mPostponedList.size();
3307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = count - 1; i >= 0; i--) {
3317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp postponed = mPostponedList.get(i);
3327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (postponed.cmd == UpdateOp.MOVE) {
3337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                int start, end;
3347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (postponed.positionStart < postponed.itemCount) {
3357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    start = postponed.positionStart;
3367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    end = postponed.itemCount;
3377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                } else {
3387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    start = postponed.itemCount;
3397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    end = postponed.positionStart;
3407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
3417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (pos >= start && pos <= end) {
3427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    //i'm affected
3437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (start == postponed.positionStart) {
3447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        if (cmd == UpdateOp.ADD) {
3457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            postponed.itemCount++;
3467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        } else if (cmd == UpdateOp.REMOVE) {
3477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            postponed.itemCount--;
3487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        }
3497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        // op moved to left, move it right to revert
3507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        pos++;
3517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    } else {
3527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        if (cmd == UpdateOp.ADD) {
3537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            postponed.positionStart++;
3547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        } else if (cmd == UpdateOp.REMOVE) {
3557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            postponed.positionStart--;
3567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        }
3577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        // op was moved right, move left to revert
3587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        pos--;
3597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
3607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                } else if (pos < postponed.positionStart) {
3617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    // postponed MV is outside the dispatched OP. if it is before, offset
3627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (cmd == UpdateOp.ADD) {
3637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        postponed.positionStart++;
3647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        postponed.itemCount++;
3657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    } else if (cmd == UpdateOp.REMOVE) {
3667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        postponed.positionStart--;
3677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        postponed.itemCount--;
3687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
3697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
3707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else {
3717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (postponed.positionStart <= pos) {
3727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (postponed.cmd == UpdateOp.ADD) {
3737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        pos -= postponed.itemCount;
3747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    } else if (postponed.cmd == UpdateOp.REMOVE) {
3757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        pos += postponed.itemCount;
3767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
3777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                } else {
3787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (cmd == UpdateOp.ADD) {
3797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        postponed.positionStart++;
3807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    } else if (cmd == UpdateOp.REMOVE) {
3817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        postponed.positionStart--;
3827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
3837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
3847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (DEBUG) {
3867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Log.d(TAG, "dispath (step" + i + ")");
3877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Log.d(TAG, "postponed state:" + i + ", pos:" + pos);
3887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                for (UpdateOp updateOp : mPostponedList) {
3897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    Log.d(TAG, updateOp.toString());
3907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
3917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                Log.d(TAG, "----");
3927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
3937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
3947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = mPostponedList.size() - 1; i >= 0; i--) {
3957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp op = mPostponedList.get(i);
3967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (op.cmd == UpdateOp.MOVE) {
3977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (op.itemCount == op.positionStart || op.itemCount < 0) {
3987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mPostponedList.remove(i);
3997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    recycleUpdateOp(op);
4007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
4017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else if (op.itemCount <= 0) {
4027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mPostponedList.remove(i);
4037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                recycleUpdateOp(op);
4047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
4057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
4067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return pos;
4077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private boolean canFindInPreLayout(int position) {
4107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int count = mPostponedList.size();
4117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < count; i++) {
4127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp op = mPostponedList.get(i);
4137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (op.cmd == UpdateOp.MOVE) {
4147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (findPositionOffset(op.itemCount, i + 1) == position) {
4157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    return true;
4167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
4177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else if (op.cmd == UpdateOp.ADD) {
4187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // TODO optimize.
4197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                final int end = op.positionStart + op.itemCount;
4207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                for (int pos = op.positionStart; pos < end; pos++) {
4217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (findPositionOffset(pos, i + 1) == position) {
4227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        return true;
4237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
4247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
4257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
4267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
4277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return false;
4287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void applyAdd(UpdateOp op) {
4317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        postponeAndUpdateViewHolders(op);
4327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    private void postponeAndUpdateViewHolders(UpdateOp op) {
4357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (DEBUG) {
4367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            Log.d(TAG, "postponing " + op);
4377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
4387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mPostponedList.add(op);
4397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        switch (op.cmd) {
4407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.ADD:
4417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
4427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
4437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.MOVE:
4447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
4457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
4467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.REMOVE:
4477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
4487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        op.itemCount);
4497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
4507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            case UpdateOp.UPDATE:
4517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
4527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                break;
4537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            default:
4547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                throw new IllegalArgumentException("Unknown update op type for " + op);
4557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
4567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    boolean hasPendingUpdates() {
4597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return mPendingUpdates.size() > 0;
4607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    boolean hasAnyUpdateTypes(int updateTypes) {
4637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return (mExistingUpdateTypes & updateTypes) != 0;
4647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    int findPositionOffset(int position) {
4677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return findPositionOffset(position, 0);
4687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    int findPositionOffset(int position, int firstPostponedItem) {
4717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int count = mPostponedList.size();
4727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = firstPostponedItem; i < count; ++i) {
4737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp op = mPostponedList.get(i);
4747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (op.cmd == UpdateOp.MOVE) {
4757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (op.positionStart == position) {
4767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    position = op.itemCount;
4777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                } else {
4787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (op.positionStart < position) {
4797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        position--; // like a remove
4807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
4817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (op.itemCount <= position) {
4827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        position++; // like an add
4837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
4847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
4857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else if (op.positionStart <= position) {
4867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (op.cmd == UpdateOp.REMOVE) {
4877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (position < op.positionStart + op.itemCount) {
4887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        return -1;
4897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
4907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    position -= op.itemCount;
4917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                } else if (op.cmd == UpdateOp.ADD) {
4927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    position += op.itemCount;
4937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
4947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
4957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
4967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return position;
4977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
4987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
4997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
5007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * @return True if updates should be processed.
5017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
5027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
5037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (itemCount < 1) {
5047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return false;
5057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
5067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
5077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mExistingUpdateTypes |= UpdateOp.UPDATE;
5087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return mPendingUpdates.size() == 1;
5097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
5107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
5117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
5127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * @return True if updates should be processed.
5137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
5147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    boolean onItemRangeInserted(int positionStart, int itemCount) {
5157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (itemCount < 1) {
5167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return false;
5177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
5187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount, null));
5197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mExistingUpdateTypes |= UpdateOp.ADD;
5207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return mPendingUpdates.size() == 1;
5217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
5227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
5237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
5247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * @return True if updates should be processed.
5257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
5267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    boolean onItemRangeRemoved(int positionStart, int itemCount) {
5277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (itemCount < 1) {
5287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return false;
5297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
5307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
5317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mExistingUpdateTypes |= UpdateOp.REMOVE;
5327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return mPendingUpdates.size() == 1;
5337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
5347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
5357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
5367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * @return True if updates should be processed.
5377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
5387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    boolean onItemRangeMoved(int from, int to, int itemCount) {
5397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (from == to) {
5407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return false; // no-op
5417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
5427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (itemCount != 1) {
5437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            throw new IllegalArgumentException("Moving more than 1 item is not supported yet");
5447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
5457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to, null));
5467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mExistingUpdateTypes |= UpdateOp.MOVE;
5477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return mPendingUpdates.size() == 1;
5487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
5497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
5507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
5517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * Skips pre-processing and applies all updates in one pass.
5527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
5537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void consumeUpdatesInOnePass() {
5547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // we still consume postponed updates (if there is) in case there was a pre-process call
5557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // w/o a matching consumePostponedUpdates.
5567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        consumePostponedUpdates();
5577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int count = mPendingUpdates.size();
5587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < count; i++) {
5597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp op = mPendingUpdates.get(i);
5607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            switch (op.cmd) {
5617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.ADD:
5627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.onDispatchSecondPass(op);
5637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
5647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
5657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.REMOVE:
5667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.onDispatchSecondPass(op);
5677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount);
5687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
5697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.UPDATE:
5707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.onDispatchSecondPass(op);
5717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
5727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
5737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.MOVE:
5747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.onDispatchSecondPass(op);
5757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
5767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
5777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
5787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (mOnItemProcessedCallback != null) {
5797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                mOnItemProcessedCallback.run();
5807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
5817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
5827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        recycleUpdateOpsAndClearList(mPendingUpdates);
5837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        mExistingUpdateTypes = 0;
5847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
5857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
5867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    public int applyPendingUpdatesToPosition(int position) {
5877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int size = mPendingUpdates.size();
5887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < size; i++) {
5897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp op = mPendingUpdates.get(i);
5907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            switch (op.cmd) {
5917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.ADD:
5927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (op.positionStart <= position) {
5937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        position += op.itemCount;
5947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
5957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
5967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.REMOVE:
5977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (op.positionStart <= position) {
5987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        final int end = op.positionStart + op.itemCount;
5997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        if (end > position) {
6007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            return RecyclerView.NO_POSITION;
6017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        }
6027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        position -= op.itemCount;
6037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
6047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
6057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UpdateOp.MOVE:
6067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    if (op.positionStart == position) {
6077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        position = op.itemCount; //position end
6087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    } else {
6097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        if (op.positionStart < position) {
6107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            position -= 1;
6117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        }
6127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        if (op.itemCount <= position) {
6137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                            position += 1;
6147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                        }
6157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    }
6167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    break;
6177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
6187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
6197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return position;
6207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
6217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    boolean hasUpdates() {
6237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return !mPostponedList.isEmpty() && !mPendingUpdates.isEmpty();
6247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
6257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
6277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * Queued operation to happen when child views are updated.
6287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
6297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    static class UpdateOp {
6307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        static final int ADD = 1;
6327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        static final int REMOVE = 1 << 1;
6347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        static final int UPDATE = 1 << 2;
6367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        static final int MOVE = 1 << 3;
6387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        static final int POOL_SIZE = 30;
6407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int cmd;
6427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int positionStart;
6447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        Object payload;
6467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        // holds the target position if this is a MOVE
6487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        int itemCount;
6497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        UpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
6517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            this.cmd = cmd;
6527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            this.positionStart = positionStart;
6537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            this.itemCount = itemCount;
6547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            this.payload = payload;
6557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
6567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        String cmdToString() {
6587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            switch (cmd) {
6597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case ADD:
6607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    return "add";
6617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case REMOVE:
6627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    return "rm";
6637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case UPDATE:
6647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    return "up";
6657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                case MOVE:
6667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    return "mv";
6677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
6687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return "??";
6697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
6707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        @Override
6727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public String toString() {
6737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return Integer.toHexString(System.identityHashCode(this))
6747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    + "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount
6757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    + ",p:" + payload + "]";
6767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
6777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        @Override
6797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public boolean equals(Object o) {
6807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (this == o) {
6817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return true;
6827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
6837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (o == null || getClass() != o.getClass()) {
6847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return false;
6857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
6867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            UpdateOp op = (UpdateOp) o;
6887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
6897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (cmd != op.cmd) {
6907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return false;
6917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
6927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) {
6937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                // reverse of this is also true
6947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (itemCount == op.positionStart && positionStart == op.itemCount) {
6957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    return true;
6967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
6977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
6987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (itemCount != op.itemCount) {
6997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return false;
7007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
7017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (positionStart != op.positionStart) {
7027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return false;
7037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
7047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            if (payload != null) {
7057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                if (!payload.equals(op.payload)) {
7067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                    return false;
7077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                }
7087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            } else if (op.payload != null) {
7097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas                return false;
7107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            }
7117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return true;
7137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
7147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        @Override
7167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        public int hashCode() {
7177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            int result = cmd;
7187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            result = 31 * result + positionStart;
7197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            result = 31 * result + itemCount;
7207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            return result;
7217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
7227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
7237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    @Override
7257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
7267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        UpdateOp op = mUpdateOpPool.acquire();
7277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (op == null) {
7287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op = new UpdateOp(cmd, positionStart, itemCount, payload);
7297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        } else {
7307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op.cmd = cmd;
7317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op.positionStart = positionStart;
7327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op.itemCount = itemCount;
7337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op.payload = payload;
7347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
7357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        return op;
7367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
7377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    @Override
7397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    public void recycleUpdateOp(UpdateOp op) {
7407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        if (!mDisableRecycler) {
7417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            op.payload = null;
7427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            mUpdateOpPool.release(op);
7437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
7447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
7457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    void recycleUpdateOpsAndClearList(List<UpdateOp> ops) {
7477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        final int count = ops.size();
7487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        for (int i = 0; i < count; i++) {
7497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas            recycleUpdateOp(ops.get(i));
7507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        }
7517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        ops.clear();
7527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
7537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    /**
7557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     * Contract between AdapterHelper and RecyclerView.
7567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas     */
7577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    interface Callback {
7587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        RecyclerView.ViewHolder findViewHolder(int position);
7607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void offsetPositionsForRemovingInvisible(int positionStart, int itemCount);
7627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount);
7647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void markViewHoldersUpdated(int positionStart, int itemCount, Object payloads);
7667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void onDispatchFirstPass(UpdateOp updateOp);
7687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void onDispatchSecondPass(UpdateOp updateOp);
7707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void offsetPositionsForAdd(int positionStart, int itemCount);
7727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas
7737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas        void offsetPositionsForMove(int from, int to);
7747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas    }
7757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas}
776