LinearLayoutManagerTest.java revision 6e83751247c5be0211d7bffaf057431c03dfef38
1d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar/*
2d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
3d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *
4d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * you may not use this file except in compliance with the License.
6d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * You may obtain a copy of the License at
7d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *
8d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *
10d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * See the License for the specific language governing permissions and
14d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * limitations under the License.
15d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */
16d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
17d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpackage android.support.v7.widget;
18d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
19d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.content.Context;
208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.graphics.Rect;
218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcel;
228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcelable;
238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log;
24d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View;
25504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup;
26d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
27504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.lang.ref.WeakReference;
288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field;
29d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList;
308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap;
31d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List;
328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map;
338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID;
34d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch;
35d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit;
36504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
37d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/**
398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}.
408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p>
418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation
428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling
438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing.
448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */
45d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
46d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final boolean DEBUG = false;
488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final String TAG = "LinearLayoutManagerTest";
508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
51d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    WrappedLinearLayoutManager mLayoutManager;
52d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    TestAdapter mTestAdapter;
54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    final List<Config> mBaseVariations = new ArrayList<Config>();
56d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
57d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    @Override
58d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    protected void setUp() throws Exception {
59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        super.setUp();
60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        for (int orientation : new int[]{LinearLayoutManager.VERTICAL,
61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                LinearLayoutManager.HORIZONTAL}) {
62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                for (boolean stackFromBottom : new boolean[]{false, true}) {
64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom));
65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
68d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
69d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    protected List<Config> addConfigVariation(List<Config> base, String fieldName,
718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Object... variations)
728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        List<Config> newConfigs = new ArrayList<Config>();
748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Field field = Config.class.getDeclaredField(fieldName);
758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Config config : base) {
768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            for (Object variation : variations) {
778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                Config newConfig = (Config) config.clone();
788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                field.set(newConfig, variation);
798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                newConfigs.add(newConfig);
808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        return newConfigs;
838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView = new RecyclerView(getActivity());
878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setHasFixedSize(true);
88504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)
89504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                : config.mTestAdapter;
908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setAdapter(mTestAdapter);
91d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,
92d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                config.mReverseLayout);
93d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
9449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setLayoutManager(mLayoutManager);
968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForFirstLayout) {
978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
1008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1016e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void testScrollToPositionWithPredictive() throws Throwable {
1026e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET);
1036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
1046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(3, 20);
1056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
1066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2,
1076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                LinearLayoutManager.INVALID_OFFSET);
1086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
1096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10);
1106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
1116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset)
1136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            throws Throwable {
1146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        setupByConfig(new Config(LinearLayoutManager.VERTICAL, false, false), true);
1156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.mOnLayoutListener = new OnLayoutListener() {
1176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
1186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
1196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (state.isPreLayout()) {
1206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertEquals("pending scroll position should still be pending",
1216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                            scrollPosition, mLayoutManager.mPendingScrollPosition);
1226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
1236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("pending scroll position offset should still be pending",
1246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset, mLayoutManager.mPendingScrollPositionOffset);
1256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
1266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } else {
1276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    RecyclerView.ViewHolder vh =
1286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                            mRecyclerView.findViewHolderForPosition(scrollPosition);
1296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertNotNull("scroll to position should work", vh);
1306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
1316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("scroll offset should be applied properly",
1326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getPaddingTop() + scrollOffset +
1336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                        ((RecyclerView.LayoutParams) vh.itemView
1346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                                .getLayoutParams()).topMargin,
1356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getDecoratedTop(vh.itemView));
1366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
1376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
1386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
1396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        };
1406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.expectLayouts(2);
1416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        runTestOnUiThread(new Runnable() {
1426e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
1436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            public void run() {
1446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                try {
1456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mTestAdapter.addAndNotify(0, 1);
1466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) {
1476e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPosition(scrollPosition);
1486e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    } else {
1496e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPositionWithOffset(scrollPosition,
1506e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset);
1516e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
1526e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1536e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } catch (Throwable throwable) {
1546e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    throwable.printStackTrace();
1556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
1566e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
1586e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        });
1596e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.waitForLayout(2);
1606e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        checkForMainThreadException();
1616e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
1626e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private void waitForFirstLayout() throws Throwable {
164d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
1658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(mRecyclerView);
166d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
167d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
168d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
169504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    public void testRecycleDuringAnimations() throws Throwable {
170504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
171504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(300) {
172504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
173504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
174504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
175504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final int cnt = childCount.incrementAndGet();
176504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
177504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
178504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder);
179504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
180504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return testViewHolder;
181504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
182504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
183504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        setupByConfig(new Config(LinearLayoutManager.VERTICAL, false, false).itemCount(300)
184504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                .adapter(adapter), true);
185504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
186504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() {
187504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
188504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
189504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
190504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                int cnt = childCount.decrementAndGet();
191504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
192504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap);
193504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
194504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
195504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
196504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
197504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
198504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
199504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    final int cnt = childCount.incrementAndGet();
201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    if (DEBUG) {
202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                        Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView);
203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    }
204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
208504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500);
209504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(pool);
210504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
211504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
212504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
213504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
214504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
215504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(15);
216504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
217504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
218504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
219504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
220504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
221504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
222504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now trigger lots of add again, followed by a scroll to position
223504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
224504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(5 + (i % 3) * 3, 1);
225504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(25);
226504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
227504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20);
228504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
229504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
230504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
231504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
232504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
233504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
234d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
235d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void testGetFirstLastChildrenTest() throws Throwable {
236d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        for (Config config : mBaseVariations) {
237d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            getFirstLastChildrenTest(config);
238d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
239d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
240d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
24149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testDontRecycleChildrenOnDetach() throws Throwable {
24249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(false), true);
24349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
24449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
24549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
24649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
24749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
24849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("No views are recycled", recyclerSize,
24949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
25049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
25149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
25249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
25349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
25449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testRecycleChildrenOnDetach() throws Throwable {
25549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(true), true);
25649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
25749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
25849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
25949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
26049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
26149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews(
26249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mTestAdapter.getItemViewType(0), recyclerSize + childCount);
26349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
26449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("All children should be recycled", childCount + recyclerSize,
26549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
26649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
26749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
26849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
26949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
270d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void getFirstLastChildrenTest(final Config config) throws Throwable {
2718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, true);
272d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Runnable viewInBoundsTest = new Runnable() {
273d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
274d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
275d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
276d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final String boundsLog = mLayoutManager.getBoundsLog();
277d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nfirst visible child should match traversal result\n"
278d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstVisiblePosition,
279d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstVisibleItemPosition()
280d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
281d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
282d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nfirst fully visible child should match traversal result\n"
283d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstFullyVisiblePosition,
284d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstCompletelyVisibleItemPosition()
285d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
286d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
287d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nlast visible child should match traversal result\n"
288d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastVisiblePosition,
289d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastVisibleItemPosition()
290d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
291d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
292d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nlast fully visible child should match traversal result\n"
293d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastFullyVisiblePosition,
294d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastCompletelyVisibleItemPosition()
295d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
296d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
297d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
298d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
299d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
300d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // case
301d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
302d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
303d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
304d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
305d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.smoothScrollToPosition(scrollPosition);
306d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
307d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
308d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        while (mLayoutManager.isSmoothScrolling() ||
309d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
310d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            runTestOnUiThread(viewInBoundsTest);
3118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(400);
312d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
313d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // delete all items
314d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(2);
315d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
316d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
317d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // test empty case
318d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
319d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // set a new adapter with huge items to test full bounds check
320d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
321d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
322d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final TestAdapter newAdapter = new TestAdapter(100) {
323d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
324d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
325d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    int position) {
326d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                super.onBindViewHolder(holder, position);
327d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (config.mOrientation == LinearLayoutManager.HORIZONTAL) {
328d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumWidth(totalSpace + 5);
329d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                } else {
330d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumHeight(totalSpace + 5);
331d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
332d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
333d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
334d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
335d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
336d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
337d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.setAdapter(newAdapter);
338d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
339d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
340d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
341d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
342d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
343d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
3448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void testSavedState() throws Throwable {
34549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        Thread.sleep(5000);
3468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
3478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
3488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // do nothing
3518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
3538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
3558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "doing nothing";
3568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
3588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
3598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
3618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
3628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPosition(mTestAdapter.getItemCount() * 3 / 4);
3638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
3648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
3668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
3688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position";
3698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
3718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
3728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
3748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
3758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3,
3768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                50);
3778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
3788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
3808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
3828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with positive offset";
3838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
3858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
3868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
3888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
3898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3,
3908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                -50);
3918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
3928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
3948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
3958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
3968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with negative offset";
3978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
3988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
3998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
4008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
4028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
4038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Doing nothing";
4068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
4088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
4098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
4118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // update config as well so that restore assertions will work
4128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mOrientation = 1 - config.mOrientation;
4138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setOrientation(config.mOrientation);
4148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
4188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
4198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing orientation";
4248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
4268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
4278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
4298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mStackFromEnd = !config.mStackFromEnd;
4308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
4318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
4358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return true; //stack from end should not move items on change
4368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing stack from end";
4418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
4468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mReverseLayout = !config.mReverseLayout;
4478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setReverseLayout(config.mReverseLayout);
4488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
4528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
4538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing reverse layout";
4588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
45975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                },
46075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                new PostRestoreRunnable() {
46149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
46249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
46349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach;
46449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
46549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
46649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
46749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
46849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
46949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        return true;
47049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
47149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
47249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
47349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    String describe() {
47449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        return "Change shoudl recycle children";
47549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
47649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                },
47749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                new PostRestoreRunnable() {
47875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    int position;
47975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
48075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
48175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        position = mTestAdapter.getItemCount() / 2;
48275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        mLayoutManager.scrollToPosition(position);
48375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
48475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
48575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
48675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
48775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        return mTestAdapter.getItemCount() == 0;
48875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
48975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
49075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
49175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    String describe() {
492aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                        return "Scroll to position " + position ;
49375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
49475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
49575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
49675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterReLayout(Config config) {
49775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        if (mTestAdapter.getItemCount() > 0) {
498aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                            assertEquals(config + ":scrolled view should be last completely visible",
49975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                                    position,
500aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                    config.mStackFromEnd ?
501aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                            mLayoutManager.findLastCompletelyVisibleItemPosition()
502aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                        : mLayoutManager.findFirstCompletelyVisibleItemPosition());
50375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        }
50475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
5058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
5068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
50775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        boolean[] waitForLayoutOptions = new boolean[]{true, false};
50849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300);
50949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true);
51049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        for (Config config : variations) {
5118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) {
5128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                for (boolean waitForLayout : waitForLayoutOptions) {
5138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) {
5148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable,
5158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                postRestoreRunnable);
5168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        removeRecyclerView();
5178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
5208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
5218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
5228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
5238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void savedStateTest(Config config, boolean waitForLayout,
5258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation)
5268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws Throwable {
5278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (DEBUG) {
5288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " +
5298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    config + " post layout action " + postLayoutOperation.describe() +
5308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    "post restore action " + postRestoreOperation.describe());
5318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
5328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, false);
5338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
5348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
5358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            postLayoutOperation.run();
5368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
5378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
5388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcelable savedState = mRecyclerView.onSaveInstanceState();
5398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // we append a suffix to the parcelable to test out of bounds
5408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        String parcelSuffix = UUID.randomUUID().toString();
5418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcel parcel = Parcel.obtain();
5428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState.writeToParcel(parcel, 0);
5438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.writeString(parcelSuffix);
5448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
5458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // reset for reading
5468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.setDataPosition(0);
5478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // re-create
5488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
5498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
5508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        RecyclerView restored = new RecyclerView(getActivity());
5528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // this config should be no op.
5538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(),
5548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                1 - config.mOrientation, !config.mReverseLayout);
5558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.setStackFromEnd(!config.mStackFromEnd);
5568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setLayoutManager(mLayoutManager);
5578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // use the same adapter for Rect matching
5588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setAdapter(mTestAdapter);
5598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.onRestoreInstanceState(savedState);
5608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        postRestoreOperation.onAfterRestore(config);
5618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
5628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                parcel.readString());
5638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.expectLayouts(1);
5648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(restored);
5658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.waitForLayout(2);
5668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // calculate prefix here instead of above to include post restore changes
5678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() +
5688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                "\npostRestore:" + postRestoreOperation.describe() + "\n";
5698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, reverse layout should be preserved",
5708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mReverseLayout, mLayoutManager.getReverseLayout());
5718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, orientation should be preserved",
5728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mOrientation, mLayoutManager.getOrientation());
5738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, stack from end should be preserved",
5748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mStackFromEnd, mLayoutManager.getStackFromEnd());
57549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        assertEquals(logPrefix + " on saved state, mRecycleChildrenOnDetach should be preserved",
57649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                config.mRecycleChildrenOnDetach, mLayoutManager.getRecycleChildrenOnDetach());
5778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
5788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (postRestoreOperation.shouldLayoutMatch(config)) {
5798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsEqual(
5808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix + ": on restore, previous view positions should be preserved",
5818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        before, mLayoutManager.collectChildCoordinates());
5828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
5838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsNotEqual(
5848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix
58549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + ": on restore with changes, previous view positions should NOT "
58649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + "be preserved",
5878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        before, mLayoutManager.collectChildCoordinates());
5888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
58975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar            postRestoreOperation.onAfterReLayout(config);
5908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
5918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
5928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
5948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        runTestOnUiThread(new Runnable() {
5958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            @Override
5968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            public void run() {
5978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                mLayoutManager.scrollToPositionWithOffset(position, offset);
5988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
5998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        });
6008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
6038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Map<Item, Rect> after) {
6048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Throwable throwable = null;
6058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        try {
6068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertRectSetsEqual("NOT " + message, before, after);
6078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        } catch (Throwable t) {
6088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throwable = t;
6098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertNotNull(message + "\ntwo layout should be different", throwable);
6118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
614668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        StringBuilder sb = new StringBuilder();
615668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        sb.append("checking rectangle equality.");
616668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar         sb.append("before:\n");
617668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
618668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
619668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
620668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        sb.append("after:\n");
621668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : after.entrySet()) {
622668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
6238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
624668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        message = message + "\n" + sb.toString();
6258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(message + ":\nitem counts should be equal", before.size()
6268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                , after.size());
6278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
6288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Rect afterRect = after.get(entry.getKey());
6298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertNotNull(message + ":\nSame item should be visible after simple re-layout",
6308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    afterRect);
6318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertEquals(message + ":\nItem should be laid out at the same coordinates",
6328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    entry.getValue(), afterRect);
6338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
636d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    static class VisibleChildren {
637d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
638d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstVisiblePosition = RecyclerView.NO_POSITION;
639d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;
641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
642d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastVisiblePosition = RecyclerView.NO_POSITION;
643d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
644d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;
645d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
646d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
647d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
648d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "VisibleChildren{" +
649d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "firstVisiblePosition=" + firstVisiblePosition +
650d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", firstFullyVisiblePosition=" + firstFullyVisiblePosition +
651d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastVisiblePosition=" + lastVisiblePosition +
652d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastFullyVisiblePosition=" + lastFullyVisiblePosition +
653d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
654d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
655d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
656d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
6578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostLayoutRunnable {
6588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract void run() throws Throwable;
6608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
6628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostRestoreRunnable {
6658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        void onAfterRestore(Config config) throws Throwable {
6678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
6708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        boolean shouldLayoutMatch(Config config) {
6728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return true;
6738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
67475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
67575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        void onAfterReLayout(Config config) {
67675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
67775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        };
6788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    class WrappedLinearLayoutManager extends LinearLayoutManager {
681d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
682d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        CountDownLatch layoutLatch;
683d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
6848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        OrientationHelper mSecondaryOrientation;
6858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        OnLayoutListener mOnLayoutListener;
6876e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
688d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
689d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            super(context, orientation, reverseLayout);
690d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
691d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
692d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void expectLayouts(int count) {
693d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch = new CountDownLatch(count);
694d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
695d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
696d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void waitForLayout(long timeout) throws InterruptedException {
697d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            waitForLayout(timeout, TimeUnit.SECONDS);
698d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
699d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
7008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
7018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public void setOrientation(int orientation) {
7028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            super.setOrientation(orientation);
7038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            mSecondaryOrientation = null;
7048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
7058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
707504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) {
708504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
709504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child));
710504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
711504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleView(child, recycler);
712504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
713504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
714504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
715504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {
716504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
717504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index)));
718504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
719504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleViewAt(index, recycler);
720504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
721504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
722504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
72394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar        void ensureLayoutState() {
72494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar            super.ensureLayoutState();
7258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (mSecondaryOrientation == null) {
72694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                mSecondaryOrientation = OrientationHelper.createOrientationHelper(this,
72794c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                        1 - getOrientation());
7288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
7298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
7308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
731d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {
7326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit);
733d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            assertEquals("all expected layouts should be executed at the expected time",
734d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    0, layoutLatch.getCount());
7356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            getInstrumentation().waitForIdleSync();
736d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
737d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
738d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String getBoundsLog() {
739d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            StringBuilder sb = new StringBuilder();
740d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
741d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding());
742d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("\nchildren bounds\n");
743d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int childCount = getChildCount();
744d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
745d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
746d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
747d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append("[").append("start:").append(
748d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mOrientationHelper.getDecoratedStart(child)).append(", end:")
749d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n");
750d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
751d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return sb.toString();
752d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
753d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
754d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public VisibleChildren traverseAndFindVisibleChildren() {
755d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            int childCount = getChildCount();
756d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final VisibleChildren visibleChildren = new VisibleChildren();
757d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int start = mOrientationHelper.getStartAfterPadding();
758d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int end = mOrientationHelper.getEndAfterPadding();
759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
760d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
761d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childStart = mOrientationHelper.getDecoratedStart(child);
762d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
763d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean fullyVisible = childStart >= start && childEnd <= end;
764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean hidden = childEnd <= start || childStart >= end;
765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (hidden) {
766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    continue;
767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int position = getPosition(child);
769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (fullyVisible) {
770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position < visibleChildren.firstFullyVisiblePosition ||
771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {
772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstFullyVisiblePosition = position;
773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position > visibleChildren.lastFullyVisiblePosition) {
776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.lastFullyVisiblePosition = position;
777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
778d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
779d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
780d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position < visibleChildren.firstVisiblePosition ||
781d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {
782d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.firstVisiblePosition = position;
783d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
784d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
785d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position > visibleChildren.lastVisiblePosition) {
786d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.lastVisiblePosition = position;
787d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
788d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
789d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
790d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return visibleChildren;
791d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
792d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
7938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Rect getViewBounds(View view) {
7948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (getOrientation() == HORIZONTAL) {
7958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
7968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
7978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
7988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view),
7998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view));
8008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
8018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
8028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
8038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
8048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view),
8058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view));
8068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
8078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
8098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> collectChildCoordinates() throws Throwable {
8118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
8128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            runTestOnUiThread(new Runnable() {
8138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                @Override
8148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                public void run() {
8158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    final int childCount = getChildCount();
8168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    for (int i = 0; i < childCount; i++) {
8178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        View child = getChildAt(i);
8188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child
8198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                .getLayoutParams();
8208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
8218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        items.put(vh.mBindedItem, getViewBounds(child));
8228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
8248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            });
8258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return items;
8268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
8278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
828d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
829d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
8306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            try {
8316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
8326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.before(recycler, state);
8336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
8346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                super.onLayoutChildren(recycler, state);
8356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
8366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.after(recycler, state);
8376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
8386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            } catch (Throwable t) {
8396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                postExceptionToInstrumentation(t);
8406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
841d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch.countDown();
842d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
8436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
8446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
8456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
8466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
8476e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    static class OnLayoutListener {
8486e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void before(RecyclerView.Recycler recycler, RecyclerView.State state){}
8496e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void after(RecyclerView.Recycler recycler, RecyclerView.State state){}
850d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
851d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
8528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    static class Config implements Cloneable {
853d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
8548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private static final int DEFAULT_ITEM_COUNT = 100;
855d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
856d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private boolean mStackFromEnd;
857d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
858d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int mOrientation = LinearLayoutManager.VERTICAL;
859d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
860d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        boolean mReverseLayout = false;
861d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
86249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        boolean mRecycleChildrenOnDetach = false;
86349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
864d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int mItemCount = DEFAULT_ITEM_COUNT;
865d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
866504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        TestAdapter mTestAdapter;
867504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
868d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {
869d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
870d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
871d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromEnd;
872d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
873d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
874d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config() {
875d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
876d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
877d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
878504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        Config adapter(TestAdapter adapter) {
879504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            mTestAdapter = adapter;
880504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            return this;
881504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
882504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
88349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
88449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            mRecycleChildrenOnDetach = recycleChildrenOnDetach;
88549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            return this;
88649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        }
88749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
888d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config orientation(int orientation) {
889d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
890d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
891d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
892d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
893d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config stackFromBottom(boolean stackFromBottom) {
894d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromBottom;
895d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
896d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
897d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
898d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config reverseLayout(boolean reverseLayout) {
899d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
900d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
901d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
902d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
903d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config itemCount(int itemCount) {
904d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mItemCount = itemCount;
905d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
906d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
907d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // required by convention
9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public Object clone() throws CloneNotSupportedException {
9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return super.clone();
9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
914d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
915d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
916d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "Config{" +
917d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "mStackFromEnd=" + mStackFromEnd +
918d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mOrientation=" + mOrientation +
919d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mReverseLayout=" + mReverseLayout +
92049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach +
921d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mItemCount=" + mItemCount +
922d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
923d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
924d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
925d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar}
926