LinearLayoutManagerTest.java revision 888093b0a071a99d65c01116cf703d46b21f9918
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;
23a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat;
24a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityEventCompat;
25a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityRecordCompat;
26542f1260934df280985294eaef1ec8469863281fYigit Boyarimport android.util.AttributeSet;
278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log;
28d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View;
29504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup;
30a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent;
31310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout;
32d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
33c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END;
34c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START;
35310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
36310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field;
38d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList;
398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap;
40d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List;
418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map;
428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID;
43d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch;
44d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit;
45504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
46d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/**
488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}.
498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p>
508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation
518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling
528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing.
538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */
54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
55d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final boolean DEBUG = false;
578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final String TAG = "LinearLayoutManagerTest";
598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    WrappedLinearLayoutManager mLayoutManager;
61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    TestAdapter mTestAdapter;
63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    final List<Config> mBaseVariations = new ArrayList<Config>();
65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    @Override
67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    protected void setUp() throws Exception {
68d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        super.setUp();
69310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
70d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
71d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                for (boolean stackFromBottom : new boolean[]{false, true}) {
72d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom));
73d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
74d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
75d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
76d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
77d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    protected List<Config> addConfigVariation(List<Config> base, String fieldName,
798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Object... variations)
808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        List<Config> newConfigs = new ArrayList<Config>();
828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Field field = Config.class.getDeclaredField(fieldName);
838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Config config : base) {
848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            for (Object variation : variations) {
858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                Config newConfig = (Config) config.clone();
868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                field.set(newConfig, variation);
878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                newConfigs.add(newConfig);
888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        return newConfigs;
918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
94542f1260934df280985294eaef1ec8469863281fYigit Boyar        mRecyclerView = new WrappedRecyclerView(getActivity());
95542f1260934df280985294eaef1ec8469863281fYigit Boyar
968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setHasFixedSize(true);
97504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)
98504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                : config.mTestAdapter;
998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setAdapter(mTestAdapter);
100d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,
101d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                config.mReverseLayout);
102d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
10349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
1048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setLayoutManager(mLayoutManager);
1058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForFirstLayout) {
1068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
1078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
1088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
1098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItem() throws Throwable {
1111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(
1131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
1141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverse() throws Throwable {
1171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(true), 100,
1191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                0);
1201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEnd() throws Throwable {
1231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(false), 100,
1251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
1261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEndAndReverse() throws Throwable {
1291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(true), 100,
1311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
1321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemHorizontal() throws Throwable {
1351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(
1371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
1381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverseHorizontal() throws Throwable {
1411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(true),
1431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 0);
1441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEndHorizontal() throws Throwable {
1471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(false),
1491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 99);
1501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEndAndReverseHorizontal() throws Throwable {
1531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(true), 100,
1551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
1561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    /**
1591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * This tests a regression where predictive animations were not working as expected when the
1601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * first item is removed and there aren't any more items to add from that direction.
1611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * First item refers to the default anchor item.
1621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     */
1631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void removeAnchorItemTest(final Config config, int adapterSize,
1641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final int removePos) throws Throwable {
1651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        config.adapter(new TestAdapter(adapterSize) {
1661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            @Override
1671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
1681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    int position) {
1691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                super.onBindViewHolder(holder, position);
1701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
1711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (!(lp instanceof ViewGroup.MarginLayoutParams)) {
1721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    lp = new ViewGroup.MarginLayoutParams(0, 0);
1731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    holder.itemView.setLayoutParams(lp);
1741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
1761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int maxSize;
1771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
1781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getWidth();
1791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = ViewGroup.MarginLayoutParams.FILL_PARENT;
1801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getHeight();
1821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = ViewGroup.MarginLayoutParams.FILL_PARENT;
1831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int desiredSize;
1861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (position == removePos) {
1871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it large
1881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 4;
1891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it small
1911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 8;
1921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
1941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = desiredSize;
1951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = desiredSize;
1971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
1991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        });
2001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        setupByConfig(config, true);
2011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
2021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        RecyclerView.ViewHolder toBeRemoved = null;
2031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>();
2041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < childCount; i++) {
2051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mLayoutManager.getChildAt(i);
2061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
2071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (holder.getAdapterPosition() == removePos) {
2081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeRemoved = holder;
2091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            } else {
2101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeMoved.add(holder);
2111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
2121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertNotNull("test sanity", toBeRemoved);
2141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("test sanity", childCount - 1, toBeMoved.size());
2151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator();
2161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mRecyclerView.setItemAnimator(loggingItemAnimator);
2171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.reset();
2181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.expectRunPendingAnimationsCall(1);
2191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mLayoutManager.expectLayouts(2);
2201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mTestAdapter.deleteAndNotify(removePos, 1);
2211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mLayoutManager.waitForLayout(1);
2221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.waitForPendingAnimationsCall(2);
2231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("removed child should receive remove animation",
2241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mRemoveVHs.contains(toBeRemoved));
2251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder vh : toBeMoved) {
2261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("view holder should be in moved list",
2271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(vh));
2281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>();
2301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
2311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mLayoutManager.getChildAt(i);
2321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
2331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (toBeRemoved != holder && !toBeMoved.contains(holder)) {
2341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                newHolders.add(holder);
2351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
2361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("some new children should show up for the new space", newHolders.size() > 0);
2381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("no items should receive animate add since they are not new", 0,
2391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mAddVHs.size());
2401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder holder : newHolders) {
2411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("new holder should receive a move animation",
2421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(holder));
2431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("control against adding too many children due to bad layout state preparation."
2451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(),
2461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                mRecyclerView.getChildCount() <= childCount + 3 /*1 for removed view, 2 for its size*/);
2471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
2481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
249310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    public void testKeepFocusOnRelayout() throws Throwable {
250310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true);
251310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int center = (mLayoutManager.findLastVisibleItemPosition()
252310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                - mLayoutManager.findFirstVisibleItemPosition()) / 2;
253115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(center);
254310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView);
255310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
256310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
257310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
258310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                vh.itemView.requestFocus();
259310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
260310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
261310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertTrue("view should have the focus", vh.itemView.hasFocus());
262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        // add a bunch of items right before that view, make sure it keeps its position
263310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.expectLayouts(2);
264310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int childCountToAdd = mRecyclerView.getChildCount() * 2;
265310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mTestAdapter.addAndNotify(center, childCountToAdd);
266310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        center += childCountToAdd; // offset item
267310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForLayout(2);
268310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForAnimationsToEnd(20);
269115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForLayoutPosition(center);
270310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertNotNull("focused child should stay in layout", postVH);
271310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertSame("same view holder should be kept for unchanged child", vh, postVH);
272310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertEquals("focused child's screen position should stay unchanged", top,
273310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView));
274310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
275310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
276542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepFullFocusOnResize() throws Throwable {
277542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), true);
278542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
279542f1260934df280985294eaef1ec8469863281fYigit Boyar
280542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepPartialFocusOnResize() throws Throwable {
281542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), false);
282542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
283542f1260934df280985294eaef1ec8469863281fYigit Boyar
284542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepReverseFullFocusOnResize() throws Throwable {
285542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), true);
286542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
287542f1260934df280985294eaef1ec8469863281fYigit Boyar
288542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepReversePartialFocusOnResize() throws Throwable {
289542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), false);
290542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
291542f1260934df280985294eaef1ec8469863281fYigit Boyar
292542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepStackFromEndFullFocusOnResize() throws Throwable {
293542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), true);
294542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
295542f1260934df280985294eaef1ec8469863281fYigit Boyar
296542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepStackFromEndPartialFocusOnResize() throws Throwable {
297542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), false);
298542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
299542f1260934df280985294eaef1ec8469863281fYigit Boyar
300542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void keepFocusOnResizeTest(final Config config, boolean fullyVisible) throws Throwable {
301542f1260934df280985294eaef1ec8469863281fYigit Boyar        setupByConfig(config, true);
302542f1260934df280985294eaef1ec8469863281fYigit Boyar        final int targetPosition;
303542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (config.mStackFromEnd) {
304542f1260934df280985294eaef1ec8469863281fYigit Boyar            targetPosition = mLayoutManager.findFirstVisibleItemPosition();
305542f1260934df280985294eaef1ec8469863281fYigit Boyar        } else {
306542f1260934df280985294eaef1ec8469863281fYigit Boyar            targetPosition = mLayoutManager.findLastVisibleItemPosition();
307542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
308542f1260934df280985294eaef1ec8469863281fYigit Boyar        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
309542f1260934df280985294eaef1ec8469863281fYigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView
310542f1260934df280985294eaef1ec8469863281fYigit Boyar                .findViewHolderForLayoutPosition(targetPosition);
311542f1260934df280985294eaef1ec8469863281fYigit Boyar
312542f1260934df280985294eaef1ec8469863281fYigit Boyar        // scroll enough to offset the child
313542f1260934df280985294eaef1ec8469863281fYigit Boyar        int startMargin = helper.getDecoratedStart(vh.itemView) -
314542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getStartAfterPadding();
315542f1260934df280985294eaef1ec8469863281fYigit Boyar        int endMargin = helper.getEndAfterPadding() -
316542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedEnd(vh.itemView);
317542f1260934df280985294eaef1ec8469863281fYigit Boyar        Log.d(TAG, "initial start margin " + startMargin + " , end margin:" + endMargin);
318542f1260934df280985294eaef1ec8469863281fYigit Boyar        requestFocus(vh.itemView);
319542f1260934df280985294eaef1ec8469863281fYigit Boyar        runTestOnUiThread(new Runnable() {
320542f1260934df280985294eaef1ec8469863281fYigit Boyar            @Override
321542f1260934df280985294eaef1ec8469863281fYigit Boyar            public void run() {
322542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("view should gain the focus", vh.itemView.hasFocus());
323542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
324542f1260934df280985294eaef1ec8469863281fYigit Boyar        });
325542f1260934df280985294eaef1ec8469863281fYigit Boyar        do {
326542f1260934df280985294eaef1ec8469863281fYigit Boyar            Thread.sleep(100);
327542f1260934df280985294eaef1ec8469863281fYigit Boyar        } while (mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE);
328542f1260934df280985294eaef1ec8469863281fYigit Boyar        // scroll enough to offset the child
329542f1260934df280985294eaef1ec8469863281fYigit Boyar        startMargin = helper.getDecoratedStart(vh.itemView) -
330542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getStartAfterPadding();
331542f1260934df280985294eaef1ec8469863281fYigit Boyar        endMargin = helper.getEndAfterPadding() -
332542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedEnd(vh.itemView);
333542f1260934df280985294eaef1ec8469863281fYigit Boyar
334542f1260934df280985294eaef1ec8469863281fYigit Boyar        Log.d(TAG, "start margin " + startMargin + " , end margin:" + endMargin);
335542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("View should become fully visible", startMargin >= 0 && endMargin >= 0);
336542f1260934df280985294eaef1ec8469863281fYigit Boyar
337542f1260934df280985294eaef1ec8469863281fYigit Boyar        int expectedOffset = 0;
338542f1260934df280985294eaef1ec8469863281fYigit Boyar        boolean offsetAtStart = false;
339542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (!fullyVisible) {
340542f1260934df280985294eaef1ec8469863281fYigit Boyar            // move it a bit such that it is no more fully visible
341542f1260934df280985294eaef1ec8469863281fYigit Boyar            final int childSize = helper
342542f1260934df280985294eaef1ec8469863281fYigit Boyar                    .getDecoratedMeasurement(vh.itemView);
343542f1260934df280985294eaef1ec8469863281fYigit Boyar            expectedOffset = childSize / 3;
344542f1260934df280985294eaef1ec8469863281fYigit Boyar            if (startMargin < endMargin) {
345542f1260934df280985294eaef1ec8469863281fYigit Boyar                scrollBy(expectedOffset);
346542f1260934df280985294eaef1ec8469863281fYigit Boyar                offsetAtStart = true;
347542f1260934df280985294eaef1ec8469863281fYigit Boyar            } else {
348542f1260934df280985294eaef1ec8469863281fYigit Boyar                scrollBy(-expectedOffset);
349542f1260934df280985294eaef1ec8469863281fYigit Boyar                offsetAtStart = false;
350542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
351542f1260934df280985294eaef1ec8469863281fYigit Boyar            startMargin = helper.getDecoratedStart(vh.itemView) -
352542f1260934df280985294eaef1ec8469863281fYigit Boyar                    helper.getStartAfterPadding();
353542f1260934df280985294eaef1ec8469863281fYigit Boyar            endMargin = helper.getEndAfterPadding() -
354542f1260934df280985294eaef1ec8469863281fYigit Boyar                    helper.getDecoratedEnd(vh.itemView);
355542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("test sanity, view should not be fully visible", startMargin < 0
356542f1260934df280985294eaef1ec8469863281fYigit Boyar                    || endMargin < 0);
357542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
358542f1260934df280985294eaef1ec8469863281fYigit Boyar
359542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.expectLayouts(1);
360542f1260934df280985294eaef1ec8469863281fYigit Boyar        runTestOnUiThread(new Runnable() {
361542f1260934df280985294eaef1ec8469863281fYigit Boyar            @Override
362542f1260934df280985294eaef1ec8469863281fYigit Boyar            public void run() {
363542f1260934df280985294eaef1ec8469863281fYigit Boyar                final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams();
364542f1260934df280985294eaef1ec8469863281fYigit Boyar                if (config.mOrientation == HORIZONTAL) {
365542f1260934df280985294eaef1ec8469863281fYigit Boyar                    layoutParams.width = mRecyclerView.getWidth() / 2;
366542f1260934df280985294eaef1ec8469863281fYigit Boyar                } else {
367542f1260934df280985294eaef1ec8469863281fYigit Boyar                    layoutParams.height = mRecyclerView.getHeight() / 2;
368542f1260934df280985294eaef1ec8469863281fYigit Boyar                }
369542f1260934df280985294eaef1ec8469863281fYigit Boyar                mRecyclerView.setLayoutParams(layoutParams);
370542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
371542f1260934df280985294eaef1ec8469863281fYigit Boyar        });
372542f1260934df280985294eaef1ec8469863281fYigit Boyar        Thread.sleep(100);
373542f1260934df280985294eaef1ec8469863281fYigit Boyar        // add a bunch of items right before that view, make sure it keeps its position
374542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.waitForLayout(2);
375542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.waitForAnimationsToEnd(20);
376542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("view should preserve the focus", vh.itemView.hasFocus());
377542f1260934df280985294eaef1ec8469863281fYigit Boyar        final RecyclerView.ViewHolder postVH = mRecyclerView
378542f1260934df280985294eaef1ec8469863281fYigit Boyar                .findViewHolderForLayoutPosition(targetPosition);
379542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertNotNull("focused child should stay in layout", postVH);
380542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertSame("same view holder should be kept for unchanged child", vh, postVH);
381542f1260934df280985294eaef1ec8469863281fYigit Boyar        View focused = postVH.itemView;
382542f1260934df280985294eaef1ec8469863281fYigit Boyar
383542f1260934df280985294eaef1ec8469863281fYigit Boyar        startMargin = helper.getDecoratedStart(focused) - helper.getStartAfterPadding();
384542f1260934df280985294eaef1ec8469863281fYigit Boyar        endMargin = helper.getEndAfterPadding() - helper.getDecoratedEnd(focused);
385542f1260934df280985294eaef1ec8469863281fYigit Boyar
386542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("focused child should be somewhat visible",
387542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedStart(focused) < helper.getEndAfterPadding()
388542f1260934df280985294eaef1ec8469863281fYigit Boyar                        && helper.getDecoratedEnd(focused) > helper.getStartAfterPadding());
389542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (fullyVisible) {
390542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("focused child end should stay fully visible",
391542f1260934df280985294eaef1ec8469863281fYigit Boyar                    endMargin >= 0);
392542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("focused child start should stay fully visible",
393542f1260934df280985294eaef1ec8469863281fYigit Boyar                    startMargin >= 0);
394542f1260934df280985294eaef1ec8469863281fYigit Boyar        } else {
395542f1260934df280985294eaef1ec8469863281fYigit Boyar            if (offsetAtStart) {
396542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("start should preserve its offset", startMargin < 0);
397542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("end should be visible", endMargin >= 0);
398542f1260934df280985294eaef1ec8469863281fYigit Boyar            } else {
399542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("end should preserve its offset", endMargin < 0);
400542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("start should be visible", startMargin >= 0);
401542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
402542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
403542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
404542f1260934df280985294eaef1ec8469863281fYigit Boyar
405245b9720dad47a694d16a1d0f48ad462bc27989fYigit Boyar    public void testResize() throws Throwable {
406310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5
407310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                , Config.DEFAULT_ITEM_COUNT)) {
408310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            stackFromEndTest(config);
409310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            removeRecyclerView();
410310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
411310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
412310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
413c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    public void testScrollToPositionWithOffset() throws Throwable {
414c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        for (Config config : mBaseVariations) {
415c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffsetTest(config.itemCount(300));
416c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            removeRecyclerView();
417c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
418c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
419c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
420c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    public void scrollToPositionWithOffsetTest(Config config) throws Throwable {
421c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        setupByConfig(config, true);
422c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        OrientationHelper orientationHelper = OrientationHelper
423c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                .createOrientationHelper(mLayoutManager, config.mOrientation);
424c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        Rect layoutBounds = getDecoratedRecyclerViewBounds();
425c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // try scrolling towards head, should not affect anything
426c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
427c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        if (config.mStackFromEnd) {
428c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(mTestAdapter.getItemCount() - 1,
429c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    mLayoutManager.mOrientationHelper.getEnd() - 500);
430c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        } else {
431c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(0, 20);
432c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
433c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        assertRectSetsEqual(config + " trying to over scroll with offset should be no-op",
434c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                before, mLayoutManager.collectChildCoordinates());
435c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // try offsetting some visible children
436c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        int testCount = 10;
437c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        while (testCount-- > 0) {
438c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            // get middle child
439c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2);
440115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            final int position = mRecyclerView.getChildLayoutPosition(child);
441c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int startOffset = config.mReverseLayout ?
442c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    orientationHelper.getEndAfterPadding() - orientationHelper
443c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getDecoratedEnd(child)
444c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    : orientationHelper.getDecoratedStart(child) - orientationHelper
445c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getStartAfterPadding();
446c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int scrollOffset = config.mStackFromEnd ? startOffset + startOffset / 2
447c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    : startOffset / 2;
448c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.expectLayouts(1);
449c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(position, scrollOffset);
450c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.waitForLayout(2);
451c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int finalOffset = config.mReverseLayout ?
452c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    orientationHelper.getEndAfterPadding() - orientationHelper
453c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getDecoratedEnd(child)
454c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    : orientationHelper.getDecoratedStart(child) - orientationHelper
455c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getStartAfterPadding();
456c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            assertEquals(config + " scroll with offset on a visible child should work fine " +
457c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    " offset:" + finalOffset + " , existing offset:" + startOffset + ", "
458c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            + "child " + position,
459c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    scrollOffset, finalOffset);
460c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
461c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
462c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // try scrolling to invisible children
463c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        testCount = 10;
464c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // we test above and below, one by one
465c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        int offsetMultiplier = -1;
466c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        while (testCount-- > 0) {
467c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final TargetTuple target = findInvisibleTarget(config);
468c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final String logPrefix = config + " " + target;
469c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.expectLayouts(1);
470c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int offset = offsetMultiplier
471c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3;
472c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(target.mPosition, offset);
473c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.waitForLayout(2);
474c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final View child = mLayoutManager.findViewByPosition(target.mPosition);
475c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            assertNotNull(logPrefix + " scrolling to a mPosition with offset " + offset
476c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    + " should layout it", child);
477c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final Rect bounds = mLayoutManager.getViewBounds(child);
478c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (DEBUG) {
479c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                Log.d(TAG, logPrefix + " post scroll to invisible mPosition " + bounds + " in "
480c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        + layoutBounds + " with offset " + offset);
481c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
482c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
483c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (config.mReverseLayout) {
484c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                assertEquals(logPrefix + " when scrolling with offset to an invisible in reverse "
485c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                                + "layout, its end should align with recycler view's end - offset",
486c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getEndAfterPadding() - offset,
487c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getDecoratedEnd(child)
488c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                );
489c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            } else {
490c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                assertEquals(logPrefix + " when scrolling with offset to an invisible child in normal"
491c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                                + " layout its start should align with recycler view's start + "
492c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                                + "offset",
493c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getStartAfterPadding() + offset,
494c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getDecoratedStart(child)
495c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                );
496c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
497c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            offsetMultiplier *= -1;
498c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
499c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
500c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
501c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    private TargetTuple findInvisibleTarget(Config config) {
502c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE;
503c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
504c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            View child = mLayoutManager.getChildAt(i);
505115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            int position = mRecyclerView.getChildLayoutPosition(child);
506c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (position < minPosition) {
507c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                minPosition = position;
508c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
509c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (position > maxPosition) {
510c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                maxPosition = position;
511c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
512c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
513c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int tailTarget = maxPosition +
514c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                (mRecyclerView.getAdapter().getItemCount() - maxPosition) / 2;
515c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int headTarget = minPosition / 2;
516c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int target;
517c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // where will the child come from ?
518c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int itemLayoutDirection;
519c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) {
520c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            target = tailTarget;
521c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END;
522c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        } else {
523c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            target = headTarget;
524c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START;
525c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
526c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        if (DEBUG) {
527c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            Log.d(TAG,
528c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition);
529c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
530c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        return new TargetTuple(target, itemLayoutDirection);
531c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
532c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
533310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    public void stackFromEndTest(final Config config) throws Throwable {
534310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final FrameLayout container = getRecyclerViewContainer();
535310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
536310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
537310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
538310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                container.setPadding(0, 0, 0, 0);
539310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
540310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
541310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
542310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(config, true);
543310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
544310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
545310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int lastCompletelyVisibleItemPosition = mLayoutManager.findLastCompletelyVisibleItemPosition();
546310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
547310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.expectLayouts(1);
548310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        // resize the recycler view to half
549310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
550310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
551310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
552310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                if (config.mOrientation == HORIZONTAL) {
553310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    container.setPadding(0, 0, container.getWidth() / 2, 0);
554310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                } else {
555310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    container.setPadding(0, 0, 0, container.getWidth() / 2);
556310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                }
557310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
558310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
559310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForLayout(1);
560310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        if (config.mStackFromEnd) {
561310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last visible position should not change.",
562310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition());
563310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last completely visible position should not change",
564310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    lastCompletelyVisibleItemPosition,
565310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    mLayoutManager.findLastCompletelyVisibleItemPosition());
566310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        } else {
567310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: first visible position should not change.",
568310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition());
569310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last completely visible position should not change",
570310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    firstCompletelyVisibleItemPosition,
571310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    mLayoutManager.findFirstCompletelyVisibleItemPosition());
572310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
573310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
574310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
5756e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void testScrollToPositionWithPredictive() throws Throwable {
5766e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET);
5776e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
5786e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(3, 20);
5796e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
5806e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2,
5816e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                LinearLayoutManager.INVALID_OFFSET);
5826e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
5836e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10);
5846e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
5856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
5866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset)
5876e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            throws Throwable {
588310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false), true);
5896e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
5906e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.mOnLayoutListener = new OnLayoutListener() {
5916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
5926e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
5936e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (state.isPreLayout()) {
5946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertEquals("pending scroll position should still be pending",
5956e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                            scrollPosition, mLayoutManager.mPendingScrollPosition);
5966e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
5976e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("pending scroll position offset should still be pending",
5986e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset, mLayoutManager.mPendingScrollPositionOffset);
5996e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
6006e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } else {
6016e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    RecyclerView.ViewHolder vh =
602115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            mRecyclerView.findViewHolderForLayoutPosition(scrollPosition);
6036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertNotNull("scroll to position should work", vh);
6046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
6056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("scroll offset should be applied properly",
6066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getPaddingTop() + scrollOffset +
6076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                        ((RecyclerView.LayoutParams) vh.itemView
6086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                                .getLayoutParams()).topMargin,
6096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getDecoratedTop(vh.itemView));
6106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
6116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
6126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
6136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        };
6146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.expectLayouts(2);
6156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        runTestOnUiThread(new Runnable() {
6166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
6176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            public void run() {
6186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                try {
6196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mTestAdapter.addAndNotify(0, 1);
6206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) {
6216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPosition(scrollPosition);
6226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    } else {
6236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPositionWithOffset(scrollPosition,
6246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset);
6256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
6266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
6276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } catch (Throwable throwable) {
6286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    throwable.printStackTrace();
6296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
6306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
6316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
6326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        });
6336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.waitForLayout(2);
6346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        checkForMainThreadException();
6356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
6366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
6378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private void waitForFirstLayout() throws Throwable {
638d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
6398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(mRecyclerView);
640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
642d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
643504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    public void testRecycleDuringAnimations() throws Throwable {
644504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
645504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(300) {
646504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
647504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
648504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
649504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final int cnt = childCount.incrementAndGet();
650504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
651504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
652504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder);
653504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
654504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return testViewHolder;
655504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
656504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
657310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(300)
658504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                .adapter(adapter), true);
659504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
660504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() {
661504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
662504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
663504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
664504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                int cnt = childCount.decrementAndGet();
665504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
666504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap);
667504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
668504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
669504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
670504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
671504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
672504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
673504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
674504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    final int cnt = childCount.incrementAndGet();
675504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    if (DEBUG) {
676504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                        Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView);
677504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    }
678504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
679504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
680504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
681504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
682504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500);
683504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(pool);
684504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
685504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
686504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
687504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
688504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
689504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(15);
690504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
691504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
692504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
693504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
694504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
695504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
696504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now trigger lots of add again, followed by a scroll to position
697504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
698504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(5 + (i % 3) * 3, 1);
699504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(25);
700504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
701504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20);
702504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
703504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
704504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
705504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
706504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
707504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
708d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
709d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void testGetFirstLastChildrenTest() throws Throwable {
710d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        for (Config config : mBaseVariations) {
711d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            getFirstLastChildrenTest(config);
712d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
713d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
714d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
71549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testDontRecycleChildrenOnDetach() throws Throwable {
71649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(false), true);
71749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
71849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
71949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
72049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
72149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
72249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("No views are recycled", recyclerSize,
72349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
72449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
72549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
72649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
72749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
72849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testRecycleChildrenOnDetach() throws Throwable {
72949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(true), true);
73049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
73149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
73249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
73349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
73449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
73549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews(
73649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mTestAdapter.getItemViewType(0), recyclerSize + childCount);
73749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
73849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("All children should be recycled", childCount + recyclerSize,
73949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
74049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
74149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
74249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
74349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
744d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void getFirstLastChildrenTest(final Config config) throws Throwable {
7458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, true);
746d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Runnable viewInBoundsTest = new Runnable() {
747d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
748d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
749d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
750d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final String boundsLog = mLayoutManager.getBoundsLog();
751d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nfirst visible child should match traversal result\n"
752d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstVisiblePosition,
753d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstVisibleItemPosition()
754d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
755d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
756d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nfirst fully visible child should match traversal result\n"
757d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstFullyVisiblePosition,
758d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstCompletelyVisibleItemPosition()
759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
760d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
761d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nlast visible child should match traversal result\n"
762d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastVisiblePosition,
763d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastVisibleItemPosition()
764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nlast fully visible child should match traversal result\n"
767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastFullyVisiblePosition,
768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastCompletelyVisibleItemPosition()
769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // case
775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
778d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
779d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.smoothScrollToPosition(scrollPosition);
780d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
781d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
782d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        while (mLayoutManager.isSmoothScrolling() ||
783d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
784d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            runTestOnUiThread(viewInBoundsTest);
7858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(400);
786d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
787d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // delete all items
788d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(2);
789d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
790d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
791d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // test empty case
792d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
793d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // set a new adapter with huge items to test full bounds check
794d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
795d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
796d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final TestAdapter newAdapter = new TestAdapter(100) {
797d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
798d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
799d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    int position) {
800d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                super.onBindViewHolder(holder, position);
801310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                if (config.mOrientation == HORIZONTAL) {
802d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumWidth(totalSpace + 5);
803d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                } else {
804d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumHeight(totalSpace + 5);
805d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
806d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
807d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
808d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
809d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
810d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
811d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.setAdapter(newAdapter);
812d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
813d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
814d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
815d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
816d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
817d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
8188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void testSavedState() throws Throwable {
8198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
8208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // do nothing
8248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "doing nothing";
8298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
8358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPosition(mTestAdapter.getItemCount() * 3 / 4);
8368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
8378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position";
8428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
8488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3,
8498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                50);
8508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
8518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with positive offset";
8568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
8628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3,
8638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                -50);
8648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
8658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with negative offset";
8708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
8728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
8738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
8758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
8768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Doing nothing";
8798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
8828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
8848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // update config as well so that restore assertions will work
8858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mOrientation = 1 - config.mOrientation;
8868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setOrientation(config.mOrientation);
8878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
8918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
8928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing orientation";
8978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
9008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
9028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mStackFromEnd = !config.mStackFromEnd;
9038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
9048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return true; //stack from end should not move items on change
9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing stack from end";
9148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
9168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
9178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
9198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mReverseLayout = !config.mReverseLayout;
9208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setReverseLayout(config.mReverseLayout);
9218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
9258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
9268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
9308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing reverse layout";
9318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
93275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                },
93375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                new PostRestoreRunnable() {
93449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
93549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
93649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach;
93749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
93849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
93949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
94049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
94149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
94249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        return true;
94349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
94449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
94549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
94649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    String describe() {
947b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                        return "Change should recycle children";
94849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
94949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                },
95049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                new PostRestoreRunnable() {
95175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    int position;
95275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
95375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
95475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        position = mTestAdapter.getItemCount() / 2;
95575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        mLayoutManager.scrollToPosition(position);
95675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
95775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
95875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
95975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
96075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        return mTestAdapter.getItemCount() == 0;
96175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
96275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
96375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
96475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    String describe() {
965aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                        return "Scroll to position " + position ;
96675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
96775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
96875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
96975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterReLayout(Config config) {
97075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        if (mTestAdapter.getItemCount() > 0) {
971aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                            assertEquals(config + ":scrolled view should be last completely visible",
97275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                                    position,
973aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                    config.mStackFromEnd ?
974aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                            mLayoutManager.findLastCompletelyVisibleItemPosition()
975aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                        : mLayoutManager.findFirstCompletelyVisibleItemPosition());
97675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        }
97775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
9788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
9798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
98075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        boolean[] waitForLayoutOptions = new boolean[]{true, false};
98149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300);
98249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true);
98349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        for (Config config : variations) {
9848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) {
9858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                for (boolean waitForLayout : waitForLayoutOptions) {
9868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) {
9878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable,
9888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                postRestoreRunnable);
9898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        removeRecyclerView();
9908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
9938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
9948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
9958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
9968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void savedStateTest(Config config, boolean waitForLayout,
9988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation)
9998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws Throwable {
10008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (DEBUG) {
10018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " +
10028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    config + " post layout action " + postLayoutOperation.describe() +
10038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    "post restore action " + postRestoreOperation.describe());
10048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, false);
10068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
10078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
10088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            postLayoutOperation.run();
10098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
10118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcelable savedState = mRecyclerView.onSaveInstanceState();
10128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // we append a suffix to the parcelable to test out of bounds
10138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        String parcelSuffix = UUID.randomUUID().toString();
10148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcel parcel = Parcel.obtain();
10158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState.writeToParcel(parcel, 0);
10168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.writeString(parcelSuffix);
10178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
10188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // reset for reading
10198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.setDataPosition(0);
10208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // re-create
10218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
10228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
10238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
10248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        RecyclerView restored = new RecyclerView(getActivity());
10258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // this config should be no op.
10268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(),
1027b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar                config.mOrientation, config.mReverseLayout);
1028b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
10298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setLayoutManager(mLayoutManager);
10308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // use the same adapter for Rect matching
10318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setAdapter(mTestAdapter);
10328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.onRestoreInstanceState(savedState);
10338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        postRestoreOperation.onAfterRestore(config);
10348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
10358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                parcel.readString());
10368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.expectLayouts(1);
10378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(restored);
10388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.waitForLayout(2);
10398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // calculate prefix here instead of above to include post restore changes
10408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() +
10418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                "\npostRestore:" + postRestoreOperation.describe() + "\n";
10428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, reverse layout should be preserved",
10438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mReverseLayout, mLayoutManager.getReverseLayout());
10448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, orientation should be preserved",
10458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mOrientation, mLayoutManager.getOrientation());
10468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, stack from end should be preserved",
10478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mStackFromEnd, mLayoutManager.getStackFromEnd());
10488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
10498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (postRestoreOperation.shouldLayoutMatch(config)) {
10508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsEqual(
10518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix + ": on restore, previous view positions should be preserved",
10528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        before, mLayoutManager.collectChildCoordinates());
10538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
10548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsNotEqual(
10558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix
105649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + ": on restore with changes, previous view positions should NOT "
105749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + "be preserved",
10588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        before, mLayoutManager.collectChildCoordinates());
10598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
106075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar            postRestoreOperation.onAfterReLayout(config);
10618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
10638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
10648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
10658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        runTestOnUiThread(new Runnable() {
10668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            @Override
10678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            public void run() {
10688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                mLayoutManager.scrollToPositionWithOffset(position, offset);
10698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
10708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        });
10718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
10728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
10738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
10748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Map<Item, Rect> after) {
10758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Throwable throwable = null;
10768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        try {
10778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertRectSetsEqual("NOT " + message, before, after);
10788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        } catch (Throwable t) {
10798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throwable = t;
10808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertNotNull(message + "\ntwo layout should be different", throwable);
10828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
10838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
10848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
1085668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        StringBuilder sb = new StringBuilder();
1086668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        sb.append("checking rectangle equality.");
1087668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar         sb.append("before:\n");
1088668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
1089668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
1090668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
1091668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        sb.append("after:\n");
1092668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : after.entrySet()) {
1093668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
10948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
1095668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        message = message + "\n" + sb.toString();
10968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(message + ":\nitem counts should be equal", before.size()
10978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                , after.size());
10988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
10998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Rect afterRect = after.get(entry.getKey());
11008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertNotNull(message + ":\nSame item should be visible after simple re-layout",
11018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    afterRect);
11028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertEquals(message + ":\nItem should be laid out at the same coordinates",
11038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    entry.getValue(), afterRect);
11048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
11058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
11068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1107a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testAccessibilityPositions() throws Throwable {
1108a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        setupByConfig(new Config(VERTICAL, false, false), true);
1109a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityDelegateCompat delegateCompat = mRecyclerView
1110a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCompatAccessibilityDelegate();
1111a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityEvent event = AccessibilityEvent.obtain();
1112a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        runTestOnUiThread(new Runnable() {
1113a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
1114a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public void run() {
1115a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event);
1116a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
1117a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        });
1118a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityRecordCompat record = AccessibilityEventCompat
1119a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .asRecord(event);
1120a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have first position",
1121a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                record.getFromIndex(),
1122a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                mLayoutManager.findFirstVisibleItemPosition());
1123a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have last position",
1124a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                record.getToIndex(),
1125a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                mLayoutManager.findLastVisibleItemPosition());
1126a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
1127a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
1128d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    static class VisibleChildren {
1129d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1130d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstVisiblePosition = RecyclerView.NO_POSITION;
1131d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1132d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;
1133d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1134d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastVisiblePosition = RecyclerView.NO_POSITION;
1135d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1136d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;
1137d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1138d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
1139d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
1140d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "VisibleChildren{" +
1141d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "firstVisiblePosition=" + firstVisiblePosition +
1142d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", firstFullyVisiblePosition=" + firstFullyVisiblePosition +
1143d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastVisiblePosition=" + lastVisiblePosition +
1144d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastFullyVisiblePosition=" + lastFullyVisiblePosition +
1145d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
1146d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1147d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
1148d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
11498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostLayoutRunnable {
11508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract void run() throws Throwable;
11528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
11548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
11558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostRestoreRunnable {
11578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        void onAfterRestore(Config config) throws Throwable {
11598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
11608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
11628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        boolean shouldLayoutMatch(Config config) {
11648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return true;
11658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
116675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
116775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        void onAfterReLayout(Config config) {
116875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
116975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        };
11708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
11718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    class WrappedLinearLayoutManager extends LinearLayoutManager {
1173d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1174d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        CountDownLatch layoutLatch;
1175d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
11768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        OrientationHelper mSecondaryOrientation;
11778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11786e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        OnLayoutListener mOnLayoutListener;
11796e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1180d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
1181d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            super(context, orientation, reverseLayout);
1182d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1183d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1184d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void expectLayouts(int count) {
1185d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch = new CountDownLatch(count);
1186d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1187d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1188d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void waitForLayout(long timeout) throws InterruptedException {
1189d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            waitForLayout(timeout, TimeUnit.SECONDS);
1190d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1191d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
11928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
11938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public void setOrientation(int orientation) {
11948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            super.setOrientation(orientation);
11958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            mSecondaryOrientation = null;
11968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
11978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
1199504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) {
1200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
1201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child));
1202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
1203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleView(child, recycler);
1204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
1205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
1207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {
1208504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
1209504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index)));
1210504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
1211504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleViewAt(index, recycler);
1212504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
1213504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1214504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
121594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar        void ensureLayoutState() {
121694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar            super.ensureLayoutState();
12178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (mSecondaryOrientation == null) {
121894c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                mSecondaryOrientation = OrientationHelper.createOrientationHelper(this,
121994c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                        1 - getOrientation());
12208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
12218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
12228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1223d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {
12246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit);
1225d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            assertEquals("all expected layouts should be executed at the expected time",
1226d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    0, layoutLatch.getCount());
12276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            getInstrumentation().waitForIdleSync();
1228d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1229d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1230888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        @Override
1231888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        LayoutState createLayoutState() {
1232888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            return new LayoutState() {
1233888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                @Override
1234888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                View next(RecyclerView.Recycler recycler) {
1235888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final boolean hadMore = hasMore(mRecyclerView.mState);
1236888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final int position = mCurrentPosition;
1237888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    View next = super.next(recycler);
1238888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("if has more, should return a view", hadMore, next != null);
1239888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("position of the returned view must match current position",
1240888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
1241888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    return next;
1242888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                }
1243888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            };
1244888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        }
1245888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
1246d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String getBoundsLog() {
1247d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            StringBuilder sb = new StringBuilder();
1248d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
1249d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding());
1250d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("\nchildren bounds\n");
1251d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int childCount = getChildCount();
1252d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
1253d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
1254d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
1255d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append("[").append("start:").append(
1256d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mOrientationHelper.getDecoratedStart(child)).append(", end:")
1257d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n");
1258d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
1259d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return sb.toString();
1260d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1261d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException {
1263310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
1264310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            if (itemAnimator == null) {
1265310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                return;
1266310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
1267310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            final CountDownLatch latch = new CountDownLatch(1);
1268310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            final boolean running = itemAnimator.isRunning(
1269310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
1270310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        @Override
1271310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        public void onAnimationsFinished() {
1272310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                            latch.countDown();
1273310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        }
1274310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    }
1275310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            );
1276310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            if (running) {
1277310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                latch.await(timeoutInSeconds, TimeUnit.SECONDS);
1278310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
1279310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
1280310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
1281d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public VisibleChildren traverseAndFindVisibleChildren() {
1282d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            int childCount = getChildCount();
1283d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final VisibleChildren visibleChildren = new VisibleChildren();
1284d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int start = mOrientationHelper.getStartAfterPadding();
1285d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int end = mOrientationHelper.getEndAfterPadding();
1286d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
1287d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
1288d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childStart = mOrientationHelper.getDecoratedStart(child);
1289d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
1290d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean fullyVisible = childStart >= start && childEnd <= end;
1291d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean hidden = childEnd <= start || childStart >= end;
1292d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (hidden) {
1293d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    continue;
1294d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1295d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int position = getPosition(child);
1296d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (fullyVisible) {
1297d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position < visibleChildren.firstFullyVisiblePosition ||
1298d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {
1299d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstFullyVisiblePosition = position;
1300d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
1301d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1302d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position > visibleChildren.lastFullyVisiblePosition) {
1303d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.lastFullyVisiblePosition = position;
1304d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
1305d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1306d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1307d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position < visibleChildren.firstVisiblePosition ||
1308d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {
1309d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.firstVisiblePosition = position;
1310d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1311d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1312d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position > visibleChildren.lastVisiblePosition) {
1313d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.lastVisiblePosition = position;
1314d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1315d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1316d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
1317d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return visibleChildren;
1318d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1319d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
13208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Rect getViewBounds(View view) {
13218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (getOrientation() == HORIZONTAL) {
13228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
13238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
13248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
13258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view),
13268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view));
13278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
13288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
13298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
13308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
13318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view),
13328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view));
13338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
13348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
13368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> collectChildCoordinates() throws Throwable {
13388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
13398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            runTestOnUiThread(new Runnable() {
13408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                @Override
13418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                public void run() {
13428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    final int childCount = getChildCount();
13438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    for (int i = 0; i < childCount; i++) {
13448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        View child = getChildAt(i);
13458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child
13468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                .getLayoutParams();
13478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
1348115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        items.put(vh.mBoundItem, getViewBounds(child));
13498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
13508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
13518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            });
13528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return items;
13538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
13548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1355d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
1356d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
13576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            try {
13586e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
13596e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.before(recycler, state);
13606e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
13616e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                super.onLayoutChildren(recycler, state);
13626e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
13636e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.after(recycler, state);
13646e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
13656e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            } catch (Throwable t) {
13666e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                postExceptionToInstrumentation(t);
13676e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
1368d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch.countDown();
1369d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
13706e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
13716e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
13726e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
13736e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
13746e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    static class OnLayoutListener {
13756e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void before(RecyclerView.Recycler recycler, RecyclerView.State state){}
13766e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void after(RecyclerView.Recycler recycler, RecyclerView.State state){}
1377d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
1378d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
13798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    static class Config implements Cloneable {
1380d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
13818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private static final int DEFAULT_ITEM_COUNT = 100;
1382d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1383d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private boolean mStackFromEnd;
1384d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1385310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int mOrientation = VERTICAL;
1386d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1387d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        boolean mReverseLayout = false;
1388d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
138949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        boolean mRecycleChildrenOnDetach = false;
139049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
1391d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int mItemCount = DEFAULT_ITEM_COUNT;
1392d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1393504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        TestAdapter mTestAdapter;
1394504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1395d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {
1396d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
1397d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
1398d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromEnd;
1399d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1400d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1401d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config() {
1402d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1403d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1404d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1405504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        Config adapter(TestAdapter adapter) {
1406504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            mTestAdapter = adapter;
1407504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            return this;
1408504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
1409504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
141049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
141149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            mRecycleChildrenOnDetach = recycleChildrenOnDetach;
141249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            return this;
141349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        }
141449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
1415d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config orientation(int orientation) {
1416d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
1417d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1418d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1419d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1420d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config stackFromBottom(boolean stackFromBottom) {
1421d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromBottom;
1422d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1423d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1424d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1425d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config reverseLayout(boolean reverseLayout) {
1426d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
1427d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1428d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1429d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1430d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config itemCount(int itemCount) {
1431d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mItemCount = itemCount;
1432d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1433d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1434d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
14358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // required by convention
14368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
14378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public Object clone() throws CloneNotSupportedException {
14388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return super.clone();
14398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
14408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1441d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
1442d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
1443d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "Config{" +
1444d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "mStackFromEnd=" + mStackFromEnd +
1445d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mOrientation=" + mOrientation +
1446d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mReverseLayout=" + mReverseLayout +
144749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach +
1448d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mItemCount=" + mItemCount +
1449d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
1450d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1451d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
1452542f1260934df280985294eaef1ec8469863281fYigit Boyar
1453542f1260934df280985294eaef1ec8469863281fYigit Boyar    private static class WrappedRecyclerView extends RecyclerView {
1454542f1260934df280985294eaef1ec8469863281fYigit Boyar
1455542f1260934df280985294eaef1ec8469863281fYigit Boyar        public WrappedRecyclerView(Context context) {
1456542f1260934df280985294eaef1ec8469863281fYigit Boyar            super(context);
1457542f1260934df280985294eaef1ec8469863281fYigit Boyar            init(context);
1458542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
1459542f1260934df280985294eaef1ec8469863281fYigit Boyar
1460542f1260934df280985294eaef1ec8469863281fYigit Boyar        public WrappedRecyclerView(Context context, AttributeSet attrs) {
1461542f1260934df280985294eaef1ec8469863281fYigit Boyar            super(context, attrs);
1462542f1260934df280985294eaef1ec8469863281fYigit Boyar            init(context);
1463542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
1464542f1260934df280985294eaef1ec8469863281fYigit Boyar
1465542f1260934df280985294eaef1ec8469863281fYigit Boyar        public WrappedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
1466542f1260934df280985294eaef1ec8469863281fYigit Boyar            super(context, attrs, defStyle);
1467542f1260934df280985294eaef1ec8469863281fYigit Boyar            init(context);
1468542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
1469542f1260934df280985294eaef1ec8469863281fYigit Boyar
1470542f1260934df280985294eaef1ec8469863281fYigit Boyar        private void init(Context context) {
1471542f1260934df280985294eaef1ec8469863281fYigit Boyar            initializeScrollbars(null);
1472542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
1473542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
1474d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar}
1475