LinearLayoutManagerTest.java revision b97e8219784e623526bc3c6077a698d608f04fd9
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;
26310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout;
27d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
28310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
29310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field;
31d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList;
328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap;
33d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List;
348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map;
358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID;
36d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch;
37d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit;
38504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
39d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/**
418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}.
428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p>
438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation
448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling
458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing.
468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */
47d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
48d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final boolean DEBUG = false;
508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final String TAG = "LinearLayoutManagerTest";
528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    WrappedLinearLayoutManager mLayoutManager;
54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    TestAdapter mTestAdapter;
56d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
57d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    final List<Config> mBaseVariations = new ArrayList<Config>();
58d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    @Override
60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    protected void setUp() throws Exception {
61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        super.setUp();
62310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                for (boolean stackFromBottom : new boolean[]{false, true}) {
65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom));
66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
68d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
69d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
70d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    protected List<Config> addConfigVariation(List<Config> base, String fieldName,
728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Object... variations)
738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        List<Config> newConfigs = new ArrayList<Config>();
758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Field field = Config.class.getDeclaredField(fieldName);
768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Config config : base) {
778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            for (Object variation : variations) {
788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                Config newConfig = (Config) config.clone();
798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                field.set(newConfig, variation);
808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                newConfigs.add(newConfig);
818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        return newConfigs;
848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView = new RecyclerView(getActivity());
888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setHasFixedSize(true);
89504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)
90504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                : config.mTestAdapter;
918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setAdapter(mTestAdapter);
92d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,
93d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                config.mReverseLayout);
94d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
9549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setLayoutManager(mLayoutManager);
978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForFirstLayout) {
988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
1008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
1018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
102310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    public void testKeepFocusOnRelayout() throws Throwable {
103310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true);
104310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int center = (mLayoutManager.findLastVisibleItemPosition()
105310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                - mLayoutManager.findFirstVisibleItemPosition()) / 2;
106310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForPosition(center);
107310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView);
108310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
109310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
110310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
111310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                vh.itemView.requestFocus();
112310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
113310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
114310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertTrue("view should have the focus", vh.itemView.hasFocus());
115310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        // add a bunch of items right before that view, make sure it keeps its position
116310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.expectLayouts(2);
117310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int childCountToAdd = mRecyclerView.getChildCount() * 2;
118310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mTestAdapter.addAndNotify(center, childCountToAdd);
119310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        center += childCountToAdd; // offset item
120310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForLayout(2);
121310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForAnimationsToEnd(20);
122310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForPosition(center);
123310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertNotNull("focused child should stay in layout", postVH);
124310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertSame("same view holder should be kept for unchanged child", vh, postVH);
125310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertEquals("focused child's screen position should stay unchanged", top,
126310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView));
127310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
128310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
129310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    public void testStackFromEnd() throws Throwable {
130310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5
131310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                , Config.DEFAULT_ITEM_COUNT)) {
132310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            stackFromEndTest(config);
133310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            removeRecyclerView();
134310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
135310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
136310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
137310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    public void stackFromEndTest(final Config config) throws Throwable {
138310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final FrameLayout container = getRecyclerViewContainer();
139310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
140310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
141310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
142310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                container.setPadding(0, 0, 0, 0);
143310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
144310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
145310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
146310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(config, true);
147310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
148310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
149310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int lastCompletelyVisibleItemPosition = mLayoutManager.findLastCompletelyVisibleItemPosition();
150310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
151310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
152310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.expectLayouts(1);
153310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        // resize the recycler view to half
154310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
155310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
156310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
157310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                if (config.mOrientation == HORIZONTAL) {
158310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    container.setPadding(0, 0, container.getWidth() / 2, 0);
159310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                } else {
160310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    container.setPadding(0, 0, 0, container.getWidth() / 2);
161310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                }
162310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
163310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
164310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForLayout(1);
165310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        if (config.mStackFromEnd) {
166310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last visible position should not change.",
167310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition());
168310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last completely visible position should not change",
169310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    lastCompletelyVisibleItemPosition,
170310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    mLayoutManager.findLastCompletelyVisibleItemPosition());
171310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        } else {
172310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: first visible position should not change.",
173310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition());
174310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last completely visible position should not change",
175310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    firstCompletelyVisibleItemPosition,
176310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    mLayoutManager.findFirstCompletelyVisibleItemPosition());
177310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
178310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
179310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
1806e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void testScrollToPositionWithPredictive() throws Throwable {
1816e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET);
1826e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
1836e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(3, 20);
1846e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
1856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2,
1866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                LinearLayoutManager.INVALID_OFFSET);
1876e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
1886e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10);
1896e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
1906e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset)
1926e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            throws Throwable {
193310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false), true);
1946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1956e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.mOnLayoutListener = new OnLayoutListener() {
1966e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
1976e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
1986e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (state.isPreLayout()) {
1996e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertEquals("pending scroll position should still be pending",
2006e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                            scrollPosition, mLayoutManager.mPendingScrollPosition);
2016e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
2026e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("pending scroll position offset should still be pending",
2036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset, mLayoutManager.mPendingScrollPositionOffset);
2046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
2056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } else {
2066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    RecyclerView.ViewHolder vh =
2076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                            mRecyclerView.findViewHolderForPosition(scrollPosition);
2086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertNotNull("scroll to position should work", vh);
2096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
2106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("scroll offset should be applied properly",
2116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getPaddingTop() + scrollOffset +
2126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                        ((RecyclerView.LayoutParams) vh.itemView
2136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                                .getLayoutParams()).topMargin,
2146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getDecoratedTop(vh.itemView));
2156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
2166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
2176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
2186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        };
2196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.expectLayouts(2);
2206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        runTestOnUiThread(new Runnable() {
2216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
2226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            public void run() {
2236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                try {
2246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mTestAdapter.addAndNotify(0, 1);
2256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) {
2266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPosition(scrollPosition);
2276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    } else {
2286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPositionWithOffset(scrollPosition,
2296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset);
2306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
2316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
2326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } catch (Throwable throwable) {
2336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    throwable.printStackTrace();
2346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
2356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
2366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
2376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        });
2386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.waitForLayout(2);
2396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        checkForMainThreadException();
2406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
2416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private void waitForFirstLayout() throws Throwable {
243d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
2448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(mRecyclerView);
245d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
246d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
247d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
248504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    public void testRecycleDuringAnimations() throws Throwable {
249504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
250504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(300) {
251504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
252504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
253504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
254504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final int cnt = childCount.incrementAndGet();
255504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
256504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
257504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder);
258504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
259504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return testViewHolder;
260504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
261504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(300)
263504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                .adapter(adapter), true);
264504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
265504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() {
266504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
267504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
268504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
269504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                int cnt = childCount.decrementAndGet();
270504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
271504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap);
272504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
273504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
274504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
275504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
276504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
277504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
278504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
279504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    final int cnt = childCount.incrementAndGet();
280504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    if (DEBUG) {
281504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                        Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView);
282504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    }
283504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
284504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
285504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
286504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
287504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500);
288504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(pool);
289504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
290504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
291504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
292504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
293504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
294504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(15);
295504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
296504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
297504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
298504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
299504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
300504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
301504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now trigger lots of add again, followed by a scroll to position
302504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
303504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(5 + (i % 3) * 3, 1);
304504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(25);
305504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
306504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20);
307504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
308504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
309504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
310504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
311504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
312504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
313d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
314d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void testGetFirstLastChildrenTest() throws Throwable {
315d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        for (Config config : mBaseVariations) {
316d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            getFirstLastChildrenTest(config);
317d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
318d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
319d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
32049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testDontRecycleChildrenOnDetach() throws Throwable {
32149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(false), true);
32249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
32349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
32449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
32549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
32649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
32749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("No views are recycled", recyclerSize,
32849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
32949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
33049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
33149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
33249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
33349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testRecycleChildrenOnDetach() throws Throwable {
33449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(true), true);
33549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
33649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
33749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
33849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
33949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
34049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews(
34149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mTestAdapter.getItemViewType(0), recyclerSize + childCount);
34249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
34349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("All children should be recycled", childCount + recyclerSize,
34449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
34549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
34649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
34749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
34849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
349d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void getFirstLastChildrenTest(final Config config) throws Throwable {
3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, true);
351d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Runnable viewInBoundsTest = new Runnable() {
352d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
353d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
354d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
355d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final String boundsLog = mLayoutManager.getBoundsLog();
356d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nfirst visible child should match traversal result\n"
357d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstVisiblePosition,
358d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstVisibleItemPosition()
359d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
360d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
361d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nfirst fully visible child should match traversal result\n"
362d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstFullyVisiblePosition,
363d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstCompletelyVisibleItemPosition()
364d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
365d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
366d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nlast visible child should match traversal result\n"
367d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastVisiblePosition,
368d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastVisibleItemPosition()
369d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
370d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
371d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nlast fully visible child should match traversal result\n"
372d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastFullyVisiblePosition,
373d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastCompletelyVisibleItemPosition()
374d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
375d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
376d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
377d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
378d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
379d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // case
380d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
381d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
382d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
383d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
384d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.smoothScrollToPosition(scrollPosition);
385d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
386d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
387d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        while (mLayoutManager.isSmoothScrolling() ||
388d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
389d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            runTestOnUiThread(viewInBoundsTest);
3908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(400);
391d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
392d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // delete all items
393d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(2);
394d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
395d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
396d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // test empty case
397d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
398d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // set a new adapter with huge items to test full bounds check
399d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
400d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
401d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final TestAdapter newAdapter = new TestAdapter(100) {
402d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
403d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
404d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    int position) {
405d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                super.onBindViewHolder(holder, position);
406310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                if (config.mOrientation == HORIZONTAL) {
407d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumWidth(totalSpace + 5);
408d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                } else {
409d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumHeight(totalSpace + 5);
410d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
411d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
412d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
413d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
414d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
415d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
416d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.setAdapter(newAdapter);
417d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
418d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
419d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
420d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
421d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
422d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
4238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void testSavedState() throws Throwable {
4248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
4258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
4268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
4288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // do nothing
4298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "doing nothing";
4348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
4368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
4378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
4398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
4408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPosition(mTestAdapter.getItemCount() * 3 / 4);
4418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
4428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position";
4478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
4498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
4508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
4528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
4538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3,
4548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                50);
4558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
4568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with positive offset";
4618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
4638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
4648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
4668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
4678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3,
4688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                -50);
4698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
4708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with negative offset";
4758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
4778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
4788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
4808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
4818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
4838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Doing nothing";
4848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
4868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
4878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
4898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // update config as well so that restore assertions will work
4908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mOrientation = 1 - config.mOrientation;
4918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setOrientation(config.mOrientation);
4928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
4958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
4968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
4978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
4988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
4998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
5008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
5018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing orientation";
5028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
5048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
5058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
5068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
5078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mStackFromEnd = !config.mStackFromEnd;
5088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
5098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
5128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
5138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return true; //stack from end should not move items on change
5148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
5178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
5188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing stack from end";
5198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
5218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
5228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
5238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
5248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mReverseLayout = !config.mReverseLayout;
5258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setReverseLayout(config.mReverseLayout);
5268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
5298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
5308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
5318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
5348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
5358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing reverse layout";
5368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
53775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                },
53875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                new PostRestoreRunnable() {
53949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
54049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
54149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach;
54249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
54349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
54449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
54549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
54649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
54749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        return true;
54849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
54949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
55049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
55149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    String describe() {
552b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                        return "Change should recycle children";
55349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
55449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                },
55549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                new PostRestoreRunnable() {
55675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    int position;
55775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
55875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
55975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        position = mTestAdapter.getItemCount() / 2;
56075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        mLayoutManager.scrollToPosition(position);
56175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
56275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
56375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
56475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
56575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        return mTestAdapter.getItemCount() == 0;
56675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
56775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
56875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
56975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    String describe() {
570aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                        return "Scroll to position " + position ;
57175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
57275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
57375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
57475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterReLayout(Config config) {
57575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        if (mTestAdapter.getItemCount() > 0) {
576aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                            assertEquals(config + ":scrolled view should be last completely visible",
57775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                                    position,
578aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                    config.mStackFromEnd ?
579aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                            mLayoutManager.findLastCompletelyVisibleItemPosition()
580aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                        : mLayoutManager.findFirstCompletelyVisibleItemPosition());
58175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        }
58275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
5838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
5848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
58575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        boolean[] waitForLayoutOptions = new boolean[]{true, false};
58649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300);
58749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true);
58849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        for (Config config : variations) {
5898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) {
5908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                for (boolean waitForLayout : waitForLayoutOptions) {
5918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) {
5928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable,
5938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                postRestoreRunnable);
5948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        removeRecyclerView();
5958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
5968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
5978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
5988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
5998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void savedStateTest(Config config, boolean waitForLayout,
6038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation)
6048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws Throwable {
6058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (DEBUG) {
6068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " +
6078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    config + " post layout action " + postLayoutOperation.describe() +
6088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    "post restore action " + postRestoreOperation.describe());
6098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, false);
6118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
6128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
6138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            postLayoutOperation.run();
6148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
6168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcelable savedState = mRecyclerView.onSaveInstanceState();
6178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // we append a suffix to the parcelable to test out of bounds
6188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        String parcelSuffix = UUID.randomUUID().toString();
6198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcel parcel = Parcel.obtain();
6208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState.writeToParcel(parcel, 0);
6218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.writeString(parcelSuffix);
6228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
6238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // reset for reading
6248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.setDataPosition(0);
6258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // re-create
6268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
6278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
6288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        RecyclerView restored = new RecyclerView(getActivity());
6308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // this config should be no op.
6318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(),
6328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                1 - config.mOrientation, !config.mReverseLayout);
6338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.setStackFromEnd(!config.mStackFromEnd);
6348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setLayoutManager(mLayoutManager);
6358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // use the same adapter for Rect matching
6368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setAdapter(mTestAdapter);
6378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.onRestoreInstanceState(savedState);
6388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        postRestoreOperation.onAfterRestore(config);
6398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
6408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                parcel.readString());
6418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.expectLayouts(1);
6428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(restored);
6438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.waitForLayout(2);
6448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // calculate prefix here instead of above to include post restore changes
6458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() +
6468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                "\npostRestore:" + postRestoreOperation.describe() + "\n";
6478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, reverse layout should be preserved",
6488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mReverseLayout, mLayoutManager.getReverseLayout());
6498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, orientation should be preserved",
6508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mOrientation, mLayoutManager.getOrientation());
6518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, stack from end should be preserved",
6528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mStackFromEnd, mLayoutManager.getStackFromEnd());
65349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        assertEquals(logPrefix + " on saved state, mRecycleChildrenOnDetach should be preserved",
65449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                config.mRecycleChildrenOnDetach, mLayoutManager.getRecycleChildrenOnDetach());
6558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
6568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (postRestoreOperation.shouldLayoutMatch(config)) {
6578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsEqual(
6588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix + ": on restore, previous view positions should be preserved",
6598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        before, mLayoutManager.collectChildCoordinates());
6608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
6618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsNotEqual(
6628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix
66349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + ": on restore with changes, previous view positions should NOT "
66449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + "be preserved",
6658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        before, mLayoutManager.collectChildCoordinates());
6668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
66775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar            postRestoreOperation.onAfterReLayout(config);
6688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
6728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        runTestOnUiThread(new Runnable() {
6738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            @Override
6748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            public void run() {
6758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                mLayoutManager.scrollToPositionWithOffset(position, offset);
6768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
6778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        });
6788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
6818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Map<Item, Rect> after) {
6828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Throwable throwable = null;
6838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        try {
6848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertRectSetsEqual("NOT " + message, before, after);
6858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        } catch (Throwable t) {
6868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throwable = t;
6878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
6888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertNotNull(message + "\ntwo layout should be different", throwable);
6898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
6908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
6918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
692668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        StringBuilder sb = new StringBuilder();
693668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        sb.append("checking rectangle equality.");
694668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar         sb.append("before:\n");
695668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
696668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
697668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
698668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        sb.append("after:\n");
699668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : after.entrySet()) {
700668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
7018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
702668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        message = message + "\n" + sb.toString();
7038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(message + ":\nitem counts should be equal", before.size()
7048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                , after.size());
7058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
7068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Rect afterRect = after.get(entry.getKey());
7078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertNotNull(message + ":\nSame item should be visible after simple re-layout",
7088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    afterRect);
7098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertEquals(message + ":\nItem should be laid out at the same coordinates",
7108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    entry.getValue(), afterRect);
7118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
7128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
7138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
714d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    static class VisibleChildren {
715d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
716d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstVisiblePosition = RecyclerView.NO_POSITION;
717d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
718d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;
719d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
720d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastVisiblePosition = RecyclerView.NO_POSITION;
721d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
722d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;
723d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
724d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
725d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
726d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "VisibleChildren{" +
727d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "firstVisiblePosition=" + firstVisiblePosition +
728d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", firstFullyVisiblePosition=" + firstFullyVisiblePosition +
729d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastVisiblePosition=" + lastVisiblePosition +
730d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastFullyVisiblePosition=" + lastFullyVisiblePosition +
731d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
732d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
733d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
734d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
7358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostLayoutRunnable {
7368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract void run() throws Throwable;
7388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
7408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
7418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostRestoreRunnable {
7438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        void onAfterRestore(Config config) throws Throwable {
7458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
7468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
7488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        boolean shouldLayoutMatch(Config config) {
7508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return true;
7518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
75275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
75375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        void onAfterReLayout(Config config) {
75475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
75575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        };
7568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
7578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    class WrappedLinearLayoutManager extends LinearLayoutManager {
759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
760d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        CountDownLatch layoutLatch;
761d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
7628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        OrientationHelper mSecondaryOrientation;
7638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7646e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        OnLayoutListener mOnLayoutListener;
7656e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            super(context, orientation, reverseLayout);
768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void expectLayouts(int count) {
771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch = new CountDownLatch(count);
772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void waitForLayout(long timeout) throws InterruptedException {
775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            waitForLayout(timeout, TimeUnit.SECONDS);
776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
7788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
7798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public void setOrientation(int orientation) {
7808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            super.setOrientation(orientation);
7818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            mSecondaryOrientation = null;
7828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
7838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
7848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
785504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) {
786504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
787504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child));
788504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
789504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleView(child, recycler);
790504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
791504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
792504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
793504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {
794504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
795504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index)));
796504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
797504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleViewAt(index, recycler);
798504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
799504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
800504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
80194c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar        void ensureLayoutState() {
80294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar            super.ensureLayoutState();
8038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (mSecondaryOrientation == null) {
80494c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                mSecondaryOrientation = OrientationHelper.createOrientationHelper(this,
80594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                        1 - getOrientation());
8068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
8078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
8088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
809d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {
8106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit);
811d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            assertEquals("all expected layouts should be executed at the expected time",
812d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    0, layoutLatch.getCount());
8136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            getInstrumentation().waitForIdleSync();
814d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
815d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
816d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String getBoundsLog() {
817d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            StringBuilder sb = new StringBuilder();
818d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
819d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding());
820d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("\nchildren bounds\n");
821d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int childCount = getChildCount();
822d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
823d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
824d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
825d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append("[").append("start:").append(
826d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mOrientationHelper.getDecoratedStart(child)).append(", end:")
827d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n");
828d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
829d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return sb.toString();
830d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
831d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
832310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException {
833310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
834310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            if (itemAnimator == null) {
835310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                return;
836310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
837310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            final CountDownLatch latch = new CountDownLatch(1);
838310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            final boolean running = itemAnimator.isRunning(
839310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
840310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        @Override
841310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        public void onAnimationsFinished() {
842310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                            latch.countDown();
843310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        }
844310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    }
845310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            );
846310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            if (running) {
847310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                latch.await(timeoutInSeconds, TimeUnit.SECONDS);
848310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
849310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
850310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
851d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public VisibleChildren traverseAndFindVisibleChildren() {
852d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            int childCount = getChildCount();
853d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final VisibleChildren visibleChildren = new VisibleChildren();
854d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int start = mOrientationHelper.getStartAfterPadding();
855d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int end = mOrientationHelper.getEndAfterPadding();
856d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
857d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
858d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childStart = mOrientationHelper.getDecoratedStart(child);
859d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
860d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean fullyVisible = childStart >= start && childEnd <= end;
861d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean hidden = childEnd <= start || childStart >= end;
862d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (hidden) {
863d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    continue;
864d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
865d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int position = getPosition(child);
866d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (fullyVisible) {
867d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position < visibleChildren.firstFullyVisiblePosition ||
868d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {
869d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstFullyVisiblePosition = position;
870d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
871d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
872d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position > visibleChildren.lastFullyVisiblePosition) {
873d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.lastFullyVisiblePosition = position;
874d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
875d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
876d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
877d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position < visibleChildren.firstVisiblePosition ||
878d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {
879d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.firstVisiblePosition = position;
880d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
881d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
882d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position > visibleChildren.lastVisiblePosition) {
883d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.lastVisiblePosition = position;
884d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
885d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
886d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
887d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return visibleChildren;
888d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
889d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
8908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Rect getViewBounds(View view) {
8918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (getOrientation() == HORIZONTAL) {
8928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
8938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
8948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
8958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view),
8968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view));
8978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
8988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
8998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
9008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
9018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view),
9028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view));
9038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
9048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
9068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> collectChildCoordinates() throws Throwable {
9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            runTestOnUiThread(new Runnable() {
9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                @Override
9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                public void run() {
9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    final int childCount = getChildCount();
9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    for (int i = 0; i < childCount; i++) {
9148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        View child = getChildAt(i);
9158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child
9168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                .getLayoutParams();
9178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
9188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        items.put(vh.mBindedItem, getViewBounds(child));
9198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
9218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            });
9228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return items;
9238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
9248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
925d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
926d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
9276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            try {
9286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
9296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.before(recycler, state);
9306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
9316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                super.onLayoutChildren(recycler, state);
9326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
9336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.after(recycler, state);
9346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
9356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            } catch (Throwable t) {
9366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                postExceptionToInstrumentation(t);
9376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
938d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch.countDown();
939d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
9406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
9416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
9426e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
9436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
9446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    static class OnLayoutListener {
9456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void before(RecyclerView.Recycler recycler, RecyclerView.State state){}
9466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void after(RecyclerView.Recycler recycler, RecyclerView.State state){}
947d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
948d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
9498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    static class Config implements Cloneable {
950d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
9518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private static final int DEFAULT_ITEM_COUNT = 100;
952d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
953d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private boolean mStackFromEnd;
954d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
955310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int mOrientation = VERTICAL;
956d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
957d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        boolean mReverseLayout = false;
958d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
95949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        boolean mRecycleChildrenOnDetach = false;
96049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
961d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int mItemCount = DEFAULT_ITEM_COUNT;
962d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
963504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        TestAdapter mTestAdapter;
964504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
965d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {
966d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
967d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
968d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromEnd;
969d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
970d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
971d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config() {
972d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
973d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
974d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
975504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        Config adapter(TestAdapter adapter) {
976504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            mTestAdapter = adapter;
977504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            return this;
978504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
979504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
98049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
98149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            mRecycleChildrenOnDetach = recycleChildrenOnDetach;
98249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            return this;
98349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        }
98449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
985d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config orientation(int orientation) {
986d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
987d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
988d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
989d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
990d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config stackFromBottom(boolean stackFromBottom) {
991d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromBottom;
992d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
993d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
994d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
995d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config reverseLayout(boolean reverseLayout) {
996d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
997d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
998d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
999d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1000d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config itemCount(int itemCount) {
1001d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mItemCount = itemCount;
1002d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1003d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1004d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
10058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // required by convention
10068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
10078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public Object clone() throws CloneNotSupportedException {
10088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return super.clone();
10098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1011d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
1012d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
1013d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "Config{" +
1014d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "mStackFromEnd=" + mStackFromEnd +
1015d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mOrientation=" + mOrientation +
1016d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mReverseLayout=" + mReverseLayout +
101749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach +
1018d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mItemCount=" + mItemCount +
1019d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
1020d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1021d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
1022d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar}
1023