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;
268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log;
27d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View;
28504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup;
29a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent;
30310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout;
31d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
32c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END;
33c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START;
34310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
35310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field;
37d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList;
388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap;
39d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List;
408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map;
418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID;
42d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch;
43d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit;
44504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
45d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/**
478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}.
488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p>
498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation
508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling
518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing.
528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */
53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final boolean DEBUG = false;
568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private static final String TAG = "LinearLayoutManagerTest";
588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    WrappedLinearLayoutManager mLayoutManager;
60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    TestAdapter mTestAdapter;
62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    final List<Config> mBaseVariations = new ArrayList<Config>();
64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    @Override
66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    protected void setUp() throws Exception {
67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        super.setUp();
68310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
69d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
70d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                for (boolean stackFromBottom : new boolean[]{false, true}) {
71d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom));
72d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
73d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
74d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
75d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
76d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    protected List<Config> addConfigVariation(List<Config> base, String fieldName,
788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Object... variations)
798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        List<Config> newConfigs = new ArrayList<Config>();
818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Field field = Config.class.getDeclaredField(fieldName);
828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Config config : base) {
838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            for (Object variation : variations) {
848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                Config newConfig = (Config) config.clone();
858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                field.set(newConfig, variation);
868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                newConfigs.add(newConfig);
878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        return newConfigs;
908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
93e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        mRecyclerView = inflateWrappedRV();
94542f1260934df280985294eaef1ec8469863281fYigit Boyar
958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setHasFixedSize(true);
96504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)
97504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                : config.mTestAdapter;
988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setAdapter(mTestAdapter);
99d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,
100d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                config.mReverseLayout);
101d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
10249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
1038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mRecyclerView.setLayoutManager(mLayoutManager);
1048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForFirstLayout) {
1058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
1068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
1078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
1088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItem() throws Throwable {
1101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(
1121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
1131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverse() throws Throwable {
1161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(true), 100,
1181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                0);
1191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEnd() throws Throwable {
1221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(false), 100,
1241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
1251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEndAndReverse() throws Throwable {
1281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(true), 100,
1301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
1311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemHorizontal() throws Throwable {
1341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(
1361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
1371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverseHorizontal() throws Throwable {
1401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(true),
1421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 0);
1431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEndHorizontal() throws Throwable {
1461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(false),
1481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 99);
1491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemStackFromEndAndReverseHorizontal() throws Throwable {
1521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(true), 100,
1541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
1551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    /**
1581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * This tests a regression where predictive animations were not working as expected when the
1591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * first item is removed and there aren't any more items to add from that direction.
1601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * First item refers to the default anchor item.
1611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     */
1621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void removeAnchorItemTest(final Config config, int adapterSize,
1631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final int removePos) throws Throwable {
1641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        config.adapter(new TestAdapter(adapterSize) {
1651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            @Override
1661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
1671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    int position) {
1681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                super.onBindViewHolder(holder, position);
1691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
1701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (!(lp instanceof ViewGroup.MarginLayoutParams)) {
1711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    lp = new ViewGroup.MarginLayoutParams(0, 0);
1721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    holder.itemView.setLayoutParams(lp);
1731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
1751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int maxSize;
1761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
1771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getWidth();
1781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = ViewGroup.MarginLayoutParams.FILL_PARENT;
1791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getHeight();
1811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = ViewGroup.MarginLayoutParams.FILL_PARENT;
1821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int desiredSize;
1851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (position == removePos) {
1861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it large
1871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 4;
1881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it small
1901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 8;
1911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
1931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = desiredSize;
1941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = desiredSize;
1961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
1981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        });
1991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        setupByConfig(config, true);
2001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
2011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        RecyclerView.ViewHolder toBeRemoved = null;
2021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>();
2031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < childCount; i++) {
2041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mLayoutManager.getChildAt(i);
2051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
2061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (holder.getAdapterPosition() == removePos) {
2071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeRemoved = holder;
2081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            } else {
2091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeMoved.add(holder);
2101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
2111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertNotNull("test sanity", toBeRemoved);
2131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("test sanity", childCount - 1, toBeMoved.size());
2141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator();
2151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mRecyclerView.setItemAnimator(loggingItemAnimator);
2161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.reset();
2171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.expectRunPendingAnimationsCall(1);
2181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mLayoutManager.expectLayouts(2);
2191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mTestAdapter.deleteAndNotify(removePos, 1);
2201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mLayoutManager.waitForLayout(1);
2211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.waitForPendingAnimationsCall(2);
2221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("removed child should receive remove animation",
2231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mRemoveVHs.contains(toBeRemoved));
2241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder vh : toBeMoved) {
2251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("view holder should be in moved list",
2261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(vh));
2271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>();
2291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
2301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mLayoutManager.getChildAt(i);
2311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
2321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (toBeRemoved != holder && !toBeMoved.contains(holder)) {
2331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                newHolders.add(holder);
2341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
2351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("some new children should show up for the new space", newHolders.size() > 0);
2371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("no items should receive animate add since they are not new", 0,
2381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mAddVHs.size());
2391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder holder : newHolders) {
2401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("new holder should receive a move animation",
2411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(holder));
2421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
2431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("control against adding too many children due to bad layout state preparation."
2441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(),
2451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                mRecyclerView.getChildCount() <= childCount + 3 /*1 for removed view, 2 for its size*/);
2461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
2471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
248310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    public void testKeepFocusOnRelayout() throws Throwable {
249310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true);
250310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int center = (mLayoutManager.findLastVisibleItemPosition()
251310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                - mLayoutManager.findFirstVisibleItemPosition()) / 2;
252115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(center);
253310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView);
254310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
255310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
256310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
257310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                vh.itemView.requestFocus();
258310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
259310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
260310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertTrue("view should have the focus", vh.itemView.hasFocus());
261310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        // add a bunch of items right before that view, make sure it keeps its position
262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.expectLayouts(2);
263310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int childCountToAdd = mRecyclerView.getChildCount() * 2;
264310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mTestAdapter.addAndNotify(center, childCountToAdd);
265310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        center += childCountToAdd; // offset item
266310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForLayout(2);
267310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForAnimationsToEnd(20);
268115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForLayoutPosition(center);
269310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertNotNull("focused child should stay in layout", postVH);
270310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertSame("same view holder should be kept for unchanged child", vh, postVH);
271310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertEquals("focused child's screen position should stay unchanged", top,
272310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView));
273310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
274310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
275542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepFullFocusOnResize() throws Throwable {
276542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), true);
277542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
278542f1260934df280985294eaef1ec8469863281fYigit Boyar
279542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepPartialFocusOnResize() throws Throwable {
280542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), false);
281542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
282542f1260934df280985294eaef1ec8469863281fYigit Boyar
283542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepReverseFullFocusOnResize() throws Throwable {
284542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), true);
285542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
286542f1260934df280985294eaef1ec8469863281fYigit Boyar
287542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepReversePartialFocusOnResize() throws Throwable {
288542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), false);
289542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
290542f1260934df280985294eaef1ec8469863281fYigit Boyar
291542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepStackFromEndFullFocusOnResize() throws Throwable {
292542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), true);
293542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
294542f1260934df280985294eaef1ec8469863281fYigit Boyar
295542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void testKeepStackFromEndPartialFocusOnResize() throws Throwable {
296542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), false);
297542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
298542f1260934df280985294eaef1ec8469863281fYigit Boyar
299542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void keepFocusOnResizeTest(final Config config, boolean fullyVisible) throws Throwable {
300542f1260934df280985294eaef1ec8469863281fYigit Boyar        setupByConfig(config, true);
301542f1260934df280985294eaef1ec8469863281fYigit Boyar        final int targetPosition;
302542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (config.mStackFromEnd) {
303542f1260934df280985294eaef1ec8469863281fYigit Boyar            targetPosition = mLayoutManager.findFirstVisibleItemPosition();
304542f1260934df280985294eaef1ec8469863281fYigit Boyar        } else {
305542f1260934df280985294eaef1ec8469863281fYigit Boyar            targetPosition = mLayoutManager.findLastVisibleItemPosition();
306542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
307542f1260934df280985294eaef1ec8469863281fYigit Boyar        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
308542f1260934df280985294eaef1ec8469863281fYigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView
309542f1260934df280985294eaef1ec8469863281fYigit Boyar                .findViewHolderForLayoutPosition(targetPosition);
310542f1260934df280985294eaef1ec8469863281fYigit Boyar
311542f1260934df280985294eaef1ec8469863281fYigit Boyar        // scroll enough to offset the child
312542f1260934df280985294eaef1ec8469863281fYigit Boyar        int startMargin = helper.getDecoratedStart(vh.itemView) -
313542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getStartAfterPadding();
314542f1260934df280985294eaef1ec8469863281fYigit Boyar        int endMargin = helper.getEndAfterPadding() -
315542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedEnd(vh.itemView);
316542f1260934df280985294eaef1ec8469863281fYigit Boyar        Log.d(TAG, "initial start margin " + startMargin + " , end margin:" + endMargin);
317542f1260934df280985294eaef1ec8469863281fYigit Boyar        requestFocus(vh.itemView);
318542f1260934df280985294eaef1ec8469863281fYigit Boyar        runTestOnUiThread(new Runnable() {
319542f1260934df280985294eaef1ec8469863281fYigit Boyar            @Override
320542f1260934df280985294eaef1ec8469863281fYigit Boyar            public void run() {
321542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("view should gain the focus", vh.itemView.hasFocus());
322542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
323542f1260934df280985294eaef1ec8469863281fYigit Boyar        });
324542f1260934df280985294eaef1ec8469863281fYigit Boyar        do {
325542f1260934df280985294eaef1ec8469863281fYigit Boyar            Thread.sleep(100);
326542f1260934df280985294eaef1ec8469863281fYigit Boyar        } while (mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE);
327542f1260934df280985294eaef1ec8469863281fYigit Boyar        // scroll enough to offset the child
328542f1260934df280985294eaef1ec8469863281fYigit Boyar        startMargin = helper.getDecoratedStart(vh.itemView) -
329542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getStartAfterPadding();
330542f1260934df280985294eaef1ec8469863281fYigit Boyar        endMargin = helper.getEndAfterPadding() -
331542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedEnd(vh.itemView);
332542f1260934df280985294eaef1ec8469863281fYigit Boyar
333542f1260934df280985294eaef1ec8469863281fYigit Boyar        Log.d(TAG, "start margin " + startMargin + " , end margin:" + endMargin);
334542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("View should become fully visible", startMargin >= 0 && endMargin >= 0);
335542f1260934df280985294eaef1ec8469863281fYigit Boyar
336542f1260934df280985294eaef1ec8469863281fYigit Boyar        int expectedOffset = 0;
337542f1260934df280985294eaef1ec8469863281fYigit Boyar        boolean offsetAtStart = false;
338542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (!fullyVisible) {
339542f1260934df280985294eaef1ec8469863281fYigit Boyar            // move it a bit such that it is no more fully visible
340542f1260934df280985294eaef1ec8469863281fYigit Boyar            final int childSize = helper
341542f1260934df280985294eaef1ec8469863281fYigit Boyar                    .getDecoratedMeasurement(vh.itemView);
342542f1260934df280985294eaef1ec8469863281fYigit Boyar            expectedOffset = childSize / 3;
343542f1260934df280985294eaef1ec8469863281fYigit Boyar            if (startMargin < endMargin) {
344542f1260934df280985294eaef1ec8469863281fYigit Boyar                scrollBy(expectedOffset);
345542f1260934df280985294eaef1ec8469863281fYigit Boyar                offsetAtStart = true;
346542f1260934df280985294eaef1ec8469863281fYigit Boyar            } else {
347542f1260934df280985294eaef1ec8469863281fYigit Boyar                scrollBy(-expectedOffset);
348542f1260934df280985294eaef1ec8469863281fYigit Boyar                offsetAtStart = false;
349542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
350542f1260934df280985294eaef1ec8469863281fYigit Boyar            startMargin = helper.getDecoratedStart(vh.itemView) -
351542f1260934df280985294eaef1ec8469863281fYigit Boyar                    helper.getStartAfterPadding();
352542f1260934df280985294eaef1ec8469863281fYigit Boyar            endMargin = helper.getEndAfterPadding() -
353542f1260934df280985294eaef1ec8469863281fYigit Boyar                    helper.getDecoratedEnd(vh.itemView);
354542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("test sanity, view should not be fully visible", startMargin < 0
355542f1260934df280985294eaef1ec8469863281fYigit Boyar                    || endMargin < 0);
356542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
357542f1260934df280985294eaef1ec8469863281fYigit Boyar
358542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.expectLayouts(1);
359542f1260934df280985294eaef1ec8469863281fYigit Boyar        runTestOnUiThread(new Runnable() {
360542f1260934df280985294eaef1ec8469863281fYigit Boyar            @Override
361542f1260934df280985294eaef1ec8469863281fYigit Boyar            public void run() {
362542f1260934df280985294eaef1ec8469863281fYigit Boyar                final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams();
363542f1260934df280985294eaef1ec8469863281fYigit Boyar                if (config.mOrientation == HORIZONTAL) {
364542f1260934df280985294eaef1ec8469863281fYigit Boyar                    layoutParams.width = mRecyclerView.getWidth() / 2;
365542f1260934df280985294eaef1ec8469863281fYigit Boyar                } else {
366542f1260934df280985294eaef1ec8469863281fYigit Boyar                    layoutParams.height = mRecyclerView.getHeight() / 2;
367542f1260934df280985294eaef1ec8469863281fYigit Boyar                }
368542f1260934df280985294eaef1ec8469863281fYigit Boyar                mRecyclerView.setLayoutParams(layoutParams);
369542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
370542f1260934df280985294eaef1ec8469863281fYigit Boyar        });
371542f1260934df280985294eaef1ec8469863281fYigit Boyar        Thread.sleep(100);
372542f1260934df280985294eaef1ec8469863281fYigit Boyar        // add a bunch of items right before that view, make sure it keeps its position
373542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.waitForLayout(2);
374542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.waitForAnimationsToEnd(20);
375542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("view should preserve the focus", vh.itemView.hasFocus());
376542f1260934df280985294eaef1ec8469863281fYigit Boyar        final RecyclerView.ViewHolder postVH = mRecyclerView
377542f1260934df280985294eaef1ec8469863281fYigit Boyar                .findViewHolderForLayoutPosition(targetPosition);
378542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertNotNull("focused child should stay in layout", postVH);
379542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertSame("same view holder should be kept for unchanged child", vh, postVH);
380542f1260934df280985294eaef1ec8469863281fYigit Boyar        View focused = postVH.itemView;
381542f1260934df280985294eaef1ec8469863281fYigit Boyar
382542f1260934df280985294eaef1ec8469863281fYigit Boyar        startMargin = helper.getDecoratedStart(focused) - helper.getStartAfterPadding();
383542f1260934df280985294eaef1ec8469863281fYigit Boyar        endMargin = helper.getEndAfterPadding() - helper.getDecoratedEnd(focused);
384542f1260934df280985294eaef1ec8469863281fYigit Boyar
385542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("focused child should be somewhat visible",
386542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedStart(focused) < helper.getEndAfterPadding()
387542f1260934df280985294eaef1ec8469863281fYigit Boyar                        && helper.getDecoratedEnd(focused) > helper.getStartAfterPadding());
388542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (fullyVisible) {
389542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("focused child end should stay fully visible",
390542f1260934df280985294eaef1ec8469863281fYigit Boyar                    endMargin >= 0);
391542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("focused child start should stay fully visible",
392542f1260934df280985294eaef1ec8469863281fYigit Boyar                    startMargin >= 0);
393542f1260934df280985294eaef1ec8469863281fYigit Boyar        } else {
394542f1260934df280985294eaef1ec8469863281fYigit Boyar            if (offsetAtStart) {
395542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("start should preserve its offset", startMargin < 0);
396542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("end should be visible", endMargin >= 0);
397542f1260934df280985294eaef1ec8469863281fYigit Boyar            } else {
398542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("end should preserve its offset", endMargin < 0);
399542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("start should be visible", startMargin >= 0);
400542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
401542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
402542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
403542f1260934df280985294eaef1ec8469863281fYigit Boyar
404245b9720dad47a694d16a1d0f48ad462bc27989fYigit Boyar    public void testResize() throws Throwable {
405310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5
406310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                , Config.DEFAULT_ITEM_COUNT)) {
407310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            stackFromEndTest(config);
408310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            removeRecyclerView();
409310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
410310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
411310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
412c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    public void testScrollToPositionWithOffset() throws Throwable {
413c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        for (Config config : mBaseVariations) {
414c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffsetTest(config.itemCount(300));
415c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            removeRecyclerView();
416c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
417c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
418c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
419c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    public void scrollToPositionWithOffsetTest(Config config) throws Throwable {
420c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        setupByConfig(config, true);
421c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        OrientationHelper orientationHelper = OrientationHelper
422c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                .createOrientationHelper(mLayoutManager, config.mOrientation);
423c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        Rect layoutBounds = getDecoratedRecyclerViewBounds();
424c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // try scrolling towards head, should not affect anything
425c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
426c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        if (config.mStackFromEnd) {
427c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(mTestAdapter.getItemCount() - 1,
428c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    mLayoutManager.mOrientationHelper.getEnd() - 500);
429c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        } else {
430c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(0, 20);
431c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
432c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        assertRectSetsEqual(config + " trying to over scroll with offset should be no-op",
433c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                before, mLayoutManager.collectChildCoordinates());
434c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // try offsetting some visible children
435c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        int testCount = 10;
436c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        while (testCount-- > 0) {
437c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            // get middle child
438c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2);
439115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            final int position = mRecyclerView.getChildLayoutPosition(child);
440c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int startOffset = config.mReverseLayout ?
441c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    orientationHelper.getEndAfterPadding() - orientationHelper
442c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getDecoratedEnd(child)
443c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    : orientationHelper.getDecoratedStart(child) - orientationHelper
444c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getStartAfterPadding();
445c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int scrollOffset = config.mStackFromEnd ? startOffset + startOffset / 2
446c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    : startOffset / 2;
447c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.expectLayouts(1);
448c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(position, scrollOffset);
449c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.waitForLayout(2);
450c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int finalOffset = config.mReverseLayout ?
451c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    orientationHelper.getEndAfterPadding() - orientationHelper
452c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getDecoratedEnd(child)
453c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    : orientationHelper.getDecoratedStart(child) - orientationHelper
454c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            .getStartAfterPadding();
455c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            assertEquals(config + " scroll with offset on a visible child should work fine " +
456c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    " offset:" + finalOffset + " , existing offset:" + startOffset + ", "
457c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                            + "child " + position,
458c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    scrollOffset, finalOffset);
459c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
460c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
461c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // try scrolling to invisible children
462c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        testCount = 10;
463c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // we test above and below, one by one
464c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        int offsetMultiplier = -1;
465c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        while (testCount-- > 0) {
466c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final TargetTuple target = findInvisibleTarget(config);
467c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final String logPrefix = config + " " + target;
468c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.expectLayouts(1);
469c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final int offset = offsetMultiplier
470c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3;
471c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            scrollToPositionWithOffset(target.mPosition, offset);
472c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            mLayoutManager.waitForLayout(2);
473c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final View child = mLayoutManager.findViewByPosition(target.mPosition);
474c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            assertNotNull(logPrefix + " scrolling to a mPosition with offset " + offset
475c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    + " should layout it", child);
476c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            final Rect bounds = mLayoutManager.getViewBounds(child);
477c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (DEBUG) {
478c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                Log.d(TAG, logPrefix + " post scroll to invisible mPosition " + bounds + " in "
479c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        + layoutBounds + " with offset " + offset);
480c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
481c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
482c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (config.mReverseLayout) {
483c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                assertEquals(logPrefix + " when scrolling with offset to an invisible in reverse "
484c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                                + "layout, its end should align with recycler view's end - offset",
485c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getEndAfterPadding() - offset,
486c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getDecoratedEnd(child)
487c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                );
488c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            } else {
489c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                assertEquals(logPrefix + " when scrolling with offset to an invisible child in normal"
490c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                                + " layout its start should align with recycler view's start + "
491c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                                + "offset",
492c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getStartAfterPadding() + offset,
493c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        orientationHelper.getDecoratedStart(child)
494c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                );
495c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
496c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            offsetMultiplier *= -1;
497c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
498c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
499c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
500c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    private TargetTuple findInvisibleTarget(Config config) {
501c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE;
502c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
503c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            View child = mLayoutManager.getChildAt(i);
504115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            int position = mRecyclerView.getChildLayoutPosition(child);
505c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (position < minPosition) {
506c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                minPosition = position;
507c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
508c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            if (position > maxPosition) {
509c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                maxPosition = position;
510c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            }
511c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
512c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int tailTarget = maxPosition +
513c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                (mRecyclerView.getAdapter().getItemCount() - maxPosition) / 2;
514c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int headTarget = minPosition / 2;
515c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int target;
516c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        // where will the child come from ?
517c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int itemLayoutDirection;
518c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) {
519c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            target = tailTarget;
520c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END;
521c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        } else {
522c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            target = headTarget;
523c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START;
524c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
525c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        if (DEBUG) {
526c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            Log.d(TAG,
527c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition);
528c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
529c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        return new TargetTuple(target, itemLayoutDirection);
530c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
531c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
532310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    public void stackFromEndTest(final Config config) throws Throwable {
533310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final FrameLayout container = getRecyclerViewContainer();
534310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
535310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
536310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
537310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                container.setPadding(0, 0, 0, 0);
538310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
539310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
540310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
541310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(config, true);
542310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
543310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
544310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int lastCompletelyVisibleItemPosition = mLayoutManager.findLastCompletelyVisibleItemPosition();
545310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
546310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.expectLayouts(1);
547310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        // resize the recycler view to half
548310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        runTestOnUiThread(new Runnable() {
549310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            @Override
550310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            public void run() {
551310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                if (config.mOrientation == HORIZONTAL) {
552310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    container.setPadding(0, 0, container.getWidth() / 2, 0);
553310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                } else {
554310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    container.setPadding(0, 0, 0, container.getWidth() / 2);
555310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                }
556310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
557310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        });
558310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForLayout(1);
559310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        if (config.mStackFromEnd) {
560310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last visible position should not change.",
561310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition());
562310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last completely visible position should not change",
563310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    lastCompletelyVisibleItemPosition,
564310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    mLayoutManager.findLastCompletelyVisibleItemPosition());
565310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        } else {
566310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: first visible position should not change.",
567310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition());
568310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            assertEquals("[" + config + "]: last completely visible position should not change",
569310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    firstCompletelyVisibleItemPosition,
570310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    mLayoutManager.findFirstCompletelyVisibleItemPosition());
571310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
572310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
573310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
5746e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void testScrollToPositionWithPredictive() throws Throwable {
5756e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET);
5766e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
5776e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(3, 20);
5786e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
5796e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2,
5806e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                LinearLayoutManager.INVALID_OFFSET);
5816e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
5826e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10);
5836e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
5846e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
5856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset)
5866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            throws Throwable {
587310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false), true);
5886e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
5896e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.mOnLayoutListener = new OnLayoutListener() {
5906e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
5916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
5926e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (state.isPreLayout()) {
5936e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertEquals("pending scroll position should still be pending",
5946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                            scrollPosition, mLayoutManager.mPendingScrollPosition);
5956e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
5966e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("pending scroll position offset should still be pending",
5976e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset, mLayoutManager.mPendingScrollPositionOffset);
5986e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
5996e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } else {
6006e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    RecyclerView.ViewHolder vh =
601115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            mRecyclerView.findViewHolderForLayoutPosition(scrollPosition);
6026e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    assertNotNull("scroll to position should work", vh);
6036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
6046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        assertEquals("scroll offset should be applied properly",
6056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getPaddingTop() + scrollOffset +
6066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                        ((RecyclerView.LayoutParams) vh.itemView
6076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                                .getLayoutParams()).topMargin,
6086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                mLayoutManager.getDecoratedTop(vh.itemView));
6096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
6106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
6116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
6126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        };
6136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.expectLayouts(2);
6146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        runTestOnUiThread(new Runnable() {
6156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            @Override
6166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            public void run() {
6176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                try {
6186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mTestAdapter.addAndNotify(0, 1);
6196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) {
6206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPosition(scrollPosition);
6216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    } else {
6226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                        mLayoutManager.scrollToPositionWithOffset(scrollPosition,
6236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                                scrollOffset);
6246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    }
6256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
6266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                } catch (Throwable throwable) {
6276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    throwable.printStackTrace();
6286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
6296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
6306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
6316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        });
6326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        mLayoutManager.waitForLayout(2);
6336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        checkForMainThreadException();
6346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
6356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
6368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    private void waitForFirstLayout() throws Throwable {
637d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
6388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(mRecyclerView);
639d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
642504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    public void testRecycleDuringAnimations() throws Throwable {
643504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
644504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(300) {
645504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
646504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
647504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
648504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final int cnt = childCount.incrementAndGet();
649504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
650504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
651504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder);
652504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
653504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return testViewHolder;
654504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
655504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
656310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(300)
657504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                .adapter(adapter), true);
658504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
659504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() {
660504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
661504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
662504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
663504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                int cnt = childCount.decrementAndGet();
664504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
665504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap);
666504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
667504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
668504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
669504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
670504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
671504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
672504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
673504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    final int cnt = childCount.incrementAndGet();
674504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    if (DEBUG) {
675504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                        Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView);
676504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    }
677504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
678504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
679504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
680504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
681504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500);
682504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(pool);
683504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
684504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
685504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
686504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
687504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
688504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(15);
689504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
690504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
691504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
692504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
693504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
694504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
695504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now trigger lots of add again, followed by a scroll to position
696504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
697504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(5 + (i % 3) * 3, 1);
698504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(25);
699504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
700504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20);
701504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
702504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
703504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
704504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
705504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
706504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
707d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
708d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void testGetFirstLastChildrenTest() throws Throwable {
709d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        for (Config config : mBaseVariations) {
710d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            getFirstLastChildrenTest(config);
711d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
712d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
713d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
71449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testDontRecycleChildrenOnDetach() throws Throwable {
71549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(false), true);
71649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
71749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
71849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
71949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
72049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
72149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("No views are recycled", recyclerSize,
72249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
72349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
72449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
72549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
72649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
72749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    public void testRecycleChildrenOnDetach() throws Throwable {
72849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(true), true);
72949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
73049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
73149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
73249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
73349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
73449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews(
73549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mTestAdapter.getItemViewType(0), recyclerSize + childCount);
73649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.setLayoutManager(new TestLayoutManager());
73749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("All children should be recycled", childCount + recyclerSize,
73849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
73949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
74049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
74149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
74249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
743d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    public void getFirstLastChildrenTest(final Config config) throws Throwable {
7448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, true);
745d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Runnable viewInBoundsTest = new Runnable() {
746d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
747d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
748d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
749d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final String boundsLog = mLayoutManager.getBoundsLog();
750d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nfirst visible child should match traversal result\n"
751d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstVisiblePosition,
752d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstVisibleItemPosition()
753d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
754d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
755d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nfirst fully visible child should match traversal result\n"
756d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.firstFullyVisiblePosition,
757d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findFirstCompletelyVisibleItemPosition()
758d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
760d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(config + ":\nlast visible child should match traversal result\n"
761d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastVisiblePosition,
762d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastVisibleItemPosition()
763d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                assertEquals(
765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        config + ":\nlast fully visible child should match traversal result\n"
766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                                + boundsLog, visibleChildren.lastFullyVisiblePosition,
767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mLayoutManager.findLastCompletelyVisibleItemPosition()
768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                );
769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // case
774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
778d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.smoothScrollToPosition(scrollPosition);
779d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
780d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
781d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        while (mLayoutManager.isSmoothScrolling() ||
782d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
783d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            runTestOnUiThread(viewInBoundsTest);
7848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(400);
785d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
786d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // delete all items
787d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(2);
788d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
789d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
790d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // test empty case
791d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
792d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        // set a new adapter with huge items to test full bounds check
793d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.expectLayouts(1);
794d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
795d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        final TestAdapter newAdapter = new TestAdapter(100) {
796d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
797d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
798d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    int position) {
799d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                super.onBindViewHolder(holder, position);
800310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                if (config.mOrientation == HORIZONTAL) {
801d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumWidth(totalSpace + 5);
802d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                } else {
803d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    holder.itemView.setMinimumHeight(totalSpace + 5);
804d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
805d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
806d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        };
807d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(new Runnable() {
808d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            @Override
809d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            public void run() {
810d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                mRecyclerView.setAdapter(newAdapter);
811d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
812d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        });
813d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        mLayoutManager.waitForLayout(2);
814d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
815d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
816d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
8178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void testSavedState() throws Throwable {
8188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
8198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // do nothing
8238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "doing nothing";
8288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
8348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPosition(mTestAdapter.getItemCount() * 3 / 4);
8358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
8368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position";
8418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
8478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3,
8488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                50);
8498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
8508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with positive offset";
8558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostLayoutRunnable() {
8588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public void run() throws Throwable {
8608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.expectLayouts(1);
8618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3,
8623d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                                -10);  // Some tests break if this value is below the item height.
8638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.waitForLayout(2);
8648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "scroll to position with negative offset";
8698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
8718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
8728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
8748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
8758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Doing nothing";
8788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
8818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
8838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        // update config as well so that restore assertions will work
8848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mOrientation = 1 - config.mOrientation;
8858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setOrientation(config.mOrientation);
8868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
8908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
8918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
8938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
8948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
8958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing orientation";
8968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
8978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
8988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
8998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
9018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mStackFromEnd = !config.mStackFromEnd;
9028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
9038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
9078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return true; //stack from end should not move items on change
9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing stack from end";
9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                },
9158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                new PostRestoreRunnable() {
9168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    void onAfterRestore(Config config) throws Throwable {
9188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        config.mReverseLayout = !config.mReverseLayout;
9198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mLayoutManager.setReverseLayout(config.mReverseLayout);
9208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    boolean shouldLayoutMatch(Config config) {
9248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return config.mItemCount == 0;
9258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    @Override
9288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    public String describe() {
9298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        return "Changing reverse layout";
9308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
93175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                },
93275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                new PostRestoreRunnable() {
93349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
93449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
93549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach;
93649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
93749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
93849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
93949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
94049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
94149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        return true;
94249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
94349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
94449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    @Override
94549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    String describe() {
946b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar                        return "Change should recycle children";
94749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    }
94849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                },
94949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                new PostRestoreRunnable() {
95075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    int position;
95175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
95275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterRestore(Config config) throws Throwable {
95375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        position = mTestAdapter.getItemCount() / 2;
95475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        mLayoutManager.scrollToPosition(position);
95575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
95675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
95775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
95875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    boolean shouldLayoutMatch(Config config) {
95975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        return mTestAdapter.getItemCount() == 0;
96075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
96175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
96275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
96375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    String describe() {
964aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                        return "Scroll to position " + position ;
96575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
96675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
96775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    @Override
96875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    void onAfterReLayout(Config config) {
96975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        if (mTestAdapter.getItemCount() > 0) {
970aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                            assertEquals(config + ":scrolled view should be last completely visible",
97175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                                    position,
972aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                    config.mStackFromEnd ?
973aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                            mLayoutManager.findLastCompletelyVisibleItemPosition()
974aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar                                        : mLayoutManager.findFirstCompletelyVisibleItemPosition());
97575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                        }
97675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar                    }
9778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
9788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        };
97975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        boolean[] waitForLayoutOptions = new boolean[]{true, false};
9803d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        boolean[] loadDataAfterRestoreOptions = 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) {
9873d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        for (boolean loadDataAfterRestore : loadDataAfterRestoreOptions) {
9883d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                            savedStateTest((Config) config.clone(), waitForLayout,
9893d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                                    loadDataAfterRestore, postLayoutRunnable, postRestoreRunnable);
9903d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                            removeRecyclerView();
9913d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        }
9928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
9938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
9958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
9968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
9978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
9988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
9993d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev    public void savedStateTest(Config config, boolean waitForLayout, boolean loadDataAfterRestore,
10008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation)
10018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws Throwable {
10028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (DEBUG) {
10038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " +
10048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    config + " post layout action " + postLayoutOperation.describe() +
10058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    "post restore action " + postRestoreOperation.describe());
10068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setupByConfig(config, false);
10088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
10098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            waitForFirstLayout();
10108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            postLayoutOperation.run();
10118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
10138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcelable savedState = mRecyclerView.onSaveInstanceState();
10148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // we append a suffix to the parcelable to test out of bounds
10158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        String parcelSuffix = UUID.randomUUID().toString();
10168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Parcel parcel = Parcel.obtain();
10178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState.writeToParcel(parcel, 0);
10188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.writeString(parcelSuffix);
10198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
10208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // reset for reading
10218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        parcel.setDataPosition(0);
10228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // re-create
10238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
10248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        removeRecyclerView();
10258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
10263d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        final int itemCount = mTestAdapter.getItemCount();
10273d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        if (loadDataAfterRestore) {
10283d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            mTestAdapter.deleteAndNotify(0, itemCount);
10293d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        }
10303d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        RecyclerView restored = new RecyclerView(getActivity());
10328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // this config should be no op.
10338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager = new WrappedLinearLayoutManager(getActivity(),
1034b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar                config.mOrientation, config.mReverseLayout);
1035b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
10368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setLayoutManager(mLayoutManager);
10378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // use the same adapter for Rect matching
10388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.setAdapter(mTestAdapter);
10398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        restored.onRestoreInstanceState(savedState);
10403d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10413d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        if (loadDataAfterRestore) {
10423d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            mTestAdapter.addAndNotify(itemCount);
10433d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        }
10443d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        postRestoreOperation.onAfterRestore(config);
10468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
10478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                parcel.readString());
10488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.expectLayouts(1);
10498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        setRecyclerView(restored);
10508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        mLayoutManager.waitForLayout(2);
10518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // calculate prefix here instead of above to include post restore changes
10528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() +
10538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                "\npostRestore:" + postRestoreOperation.describe() + "\n";
10548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, reverse layout should be preserved",
10558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mReverseLayout, mLayoutManager.getReverseLayout());
10568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, orientation should be preserved",
10578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mOrientation, mLayoutManager.getOrientation());
10588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(logPrefix + " on saved state, stack from end should be preserved",
10598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                config.mStackFromEnd, mLayoutManager.getStackFromEnd());
10608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        if (waitForLayout) {
10613d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            final boolean strictItemEquality = !loadDataAfterRestore;
10628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (postRestoreOperation.shouldLayoutMatch(config)) {
10638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsEqual(
10648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix + ": on restore, previous view positions should be preserved",
10653d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
10668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
10678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                assertRectSetsNotEqual(
10688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        logPrefix
106949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + ": on restore with changes, previous view positions should NOT "
107049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                                + "be preserved",
10713d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
10728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
107375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar            postRestoreOperation.onAfterReLayout(config);
10748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
10758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
10768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
10773d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev    public void testScrollAndClear() throws Throwable {
10783d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        setupByConfig(new Config(), true);
10793d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10803d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        assertTrue("Children not laid out", mLayoutManager.collectChildCoordinates().size() > 0);
10813d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10823d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        mLayoutManager.expectLayouts(1);
10833d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        runTestOnUiThread(new Runnable() {
10843d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            @Override
10853d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            public void run() {
10863d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                mLayoutManager.scrollToPositionWithOffset(1, 0);
10873d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                mTestAdapter.clearOnUIThread();
10883d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            }
10893d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        });
10903d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        mLayoutManager.waitForLayout(2);
10913d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10923d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        assertEquals("Remaining children", 0, mLayoutManager.collectChildCoordinates().size());
10933d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev    }
10943d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10953d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
10968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
10978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        runTestOnUiThread(new Runnable() {
10988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            @Override
10998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            public void run() {
11008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                mLayoutManager.scrollToPositionWithOffset(position, offset);
11018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
11028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        });
11038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
11048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
11063d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            Map<Item, Rect> after, boolean strictItemEquality) {
11078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Throwable throwable = null;
11088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        try {
11093d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            assertRectSetsEqual("NOT " + message, before, after, strictItemEquality);
11108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        } catch (Throwable t) {
11118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throwable = t;
11128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
11138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertNotNull(message + "\ntwo layout should be different", throwable);
11148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
11158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
11168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
11173d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        assertRectSetsEqual(message, before, after, true);
11183d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev    }
11193d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
11203d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after,
11213d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            boolean strictItemEquality) {
1122668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        StringBuilder sb = new StringBuilder();
11233d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        sb.append("checking rectangle equality.\n");
11243d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        sb.append("before:\n");
1125668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
1126668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
1127668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
1128668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        sb.append("after:\n");
1129668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (Map.Entry<Item, Rect> entry : after.entrySet()) {
1130668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
11318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
1132668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        message = message + "\n" + sb.toString();
11338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        assertEquals(message + ":\nitem counts should be equal", before.size()
11348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                , after.size());
11358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
11363d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            final Item beforeItem = entry.getKey();
11373d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            Rect afterRect = null;
11383d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            if (strictItemEquality) {
11393d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                afterRect = after.get(beforeItem);
11403d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                assertNotNull(message + ":\nSame item should be visible after simple re-layout",
11413d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        afterRect);
11423d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            } else {
11433d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) {
11443d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                    final Item afterItem = afterEntry.getKey();
11453d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                    if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) {
11463d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        afterRect = afterEntry.getValue();
11473d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        break;
11483d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                    }
11493d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                }
11503d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                assertNotNull(message + ":\nItem with same adapter index should be visible " +
11513d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                                "after simple re-layout",
11523d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                        afterRect);
11533d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            }
11548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            assertEquals(message + ":\nItem should be laid out at the same coordinates",
11558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    entry.getValue(), afterRect);
11568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
11578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
11588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1159a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testAccessibilityPositions() throws Throwable {
1160a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        setupByConfig(new Config(VERTICAL, false, false), true);
1161a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityDelegateCompat delegateCompat = mRecyclerView
1162a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCompatAccessibilityDelegate();
1163a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityEvent event = AccessibilityEvent.obtain();
1164a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        runTestOnUiThread(new Runnable() {
1165a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
1166a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public void run() {
1167a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event);
1168a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
1169a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        });
1170a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityRecordCompat record = AccessibilityEventCompat
1171a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .asRecord(event);
1172a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have first position",
1173a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                record.getFromIndex(),
1174a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                mLayoutManager.findFirstVisibleItemPosition());
1175a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have last position",
1176a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                record.getToIndex(),
1177a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                mLayoutManager.findLastVisibleItemPosition());
1178a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
1179a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
1180e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    public void testPrepareForDrop() throws Throwable {
1181e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        SelectTargetChildren[] selectors = new SelectTargetChildren[] {
1182e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                new SelectTargetChildren() {
1183e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    @Override
1184e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    public int[] selectTargetChildren(int childCount) {
1185e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                        return new int[]{1, 0};
1186e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    }
1187e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                },
1188e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                new SelectTargetChildren() {
1189e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    @Override
1190e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    public int[] selectTargetChildren(int childCount) {
1191e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                        return new int[]{0, 1};
1192e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    }
1193e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                },
1194e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                new SelectTargetChildren() {
1195e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    @Override
1196e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    public int[] selectTargetChildren(int childCount) {
1197e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                        return new int[]{childCount - 1, childCount - 2};
1198e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    }
1199e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                },
1200e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                new SelectTargetChildren() {
1201e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    @Override
1202e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    public int[] selectTargetChildren(int childCount) {
1203e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                        return new int[]{childCount - 2, childCount - 1};
1204e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    }
1205e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                },
1206e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                new SelectTargetChildren() {
1207e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    @Override
1208e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    public int[] selectTargetChildren(int childCount) {
1209e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                        return new int[]{childCount / 2, childCount / 2 + 1};
1210e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    }
1211e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                },
1212e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                new SelectTargetChildren() {
1213e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    @Override
1214e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    public int[] selectTargetChildren(int childCount) {
1215e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                        return new int[]{childCount / 2 + 1, childCount / 2};
1216e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    }
1217e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                }
1218e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        };
1219e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        for (SelectTargetChildren selector : selectors) {
1220e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            for (Config config : mBaseVariations) {
1221e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                prepareForDropTest(config, selector);
1222e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                removeRecyclerView();
1223e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            }
1224e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        }
1225e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    }
1226e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar
1227e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    public void prepareForDropTest(final Config config, SelectTargetChildren selectTargetChildren)
1228e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            throws Throwable {
1229e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        config.mTestAdapter = new TestAdapter(100) {
1230e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            @Override
1231e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
1232e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    int position) {
1233e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                super.onBindViewHolder(holder, position);
1234e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
1235e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    final int base = mRecyclerView.getWidth() / 5;
1236e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
1237e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    holder.itemView.setMinimumWidth(base + itemRand);
1238e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                } else {
1239e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    final int base = mRecyclerView.getHeight() / 5;
1240e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
1241e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    holder.itemView.setMinimumHeight(base + itemRand);
1242e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                }
1243e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            }
1244e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        };
1245e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        setupByConfig(config, true);
1246e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        mLayoutManager.expectLayouts(1);
1247e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        scrollToPosition(mTestAdapter.getItemCount() / 2);
1248e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        mLayoutManager.waitForLayout(1);
1249e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        int[] positions = selectTargetChildren.selectTargetChildren(mRecyclerView.getChildCount());
1250e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final View fromChild = mLayoutManager.getChildAt(positions[0]);
1251e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final int fromPos = mLayoutManager.getPosition(fromChild);
1252e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final View onChild = mLayoutManager.getChildAt(positions[1]);
1253e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final int toPos = mLayoutManager.getPosition(onChild);
1254e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
1255e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final int dragCoordinate;
1256e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final boolean towardsHead = toPos < fromPos;
1257e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final int referenceLine;
1258e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        if (config.mReverseLayout == towardsHead) {
1259e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            referenceLine = helper.getDecoratedEnd(onChild);
1260e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            dragCoordinate = referenceLine + 3 -
1261e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    helper.getDecoratedMeasurement(fromChild);
1262e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        } else {
1263e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            referenceLine = helper.getDecoratedStart(onChild);
1264e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            dragCoordinate = referenceLine - 3;
1265e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        }
1266e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        mLayoutManager.expectLayouts(2);
1267e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar
1268e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        final int x,y;
1269e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        if (config.mOrientation == HORIZONTAL) {
1270e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            x = dragCoordinate;
1271e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            y = fromChild.getTop();
1272e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        } else {
1273e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            y = dragCoordinate;
1274e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            x = fromChild.getLeft();
1275e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        }
1276e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        runTestOnUiThread(new Runnable() {
1277e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            @Override
1278e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            public void run() {
1279e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                mTestAdapter.moveInUIThread(fromPos, toPos);
1280e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                mTestAdapter.notifyItemMoved(fromPos, toPos);
1281e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                mLayoutManager.prepareForDrop(fromChild, onChild, x, y);
1282e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            }
1283e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        });
1284e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        mLayoutManager.waitForLayout(2);
1285e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar
1286e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        assertSame(fromChild, mRecyclerView.findViewHolderForAdapterPosition(toPos).itemView);
1287e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        // make sure it has the position we wanted
1288e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        if (config.mReverseLayout == towardsHead) {
1289e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            assertEquals(referenceLine, helper.getDecoratedEnd(fromChild));
1290e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        } else {
1291e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar            assertEquals(referenceLine, helper.getDecoratedStart(fromChild));
1292e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        }
1293e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    }
1294e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar
1295d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    static class VisibleChildren {
1296d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1297d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstVisiblePosition = RecyclerView.NO_POSITION;
1298d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1299d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;
1300d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1301d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastVisiblePosition = RecyclerView.NO_POSITION;
1302d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1303d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;
1304d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1305d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
1306d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
1307d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "VisibleChildren{" +
1308d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "firstVisiblePosition=" + firstVisiblePosition +
1309d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", firstFullyVisiblePosition=" + firstFullyVisiblePosition +
1310d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastVisiblePosition=" + lastVisiblePosition +
1311d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", lastFullyVisiblePosition=" + lastFullyVisiblePosition +
1312d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
1313d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1314d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
1315d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
13168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostLayoutRunnable {
13178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract void run() throws Throwable;
13198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
13218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
13228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    abstract private class PostRestoreRunnable {
13248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        void onAfterRestore(Config config) throws Throwable {
13268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
13278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        abstract String describe();
13298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        boolean shouldLayoutMatch(Config config) {
13318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return true;
13328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
133375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
133475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        void onAfterReLayout(Config config) {
133575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar
133675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar        };
13378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
13388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    class WrappedLinearLayoutManager extends LinearLayoutManager {
1340d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1341d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        CountDownLatch layoutLatch;
1342d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
13438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        OrientationHelper mSecondaryOrientation;
13448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        OnLayoutListener mOnLayoutListener;
13466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1347d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
1348d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            super(context, orientation, reverseLayout);
1349d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1350d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1351d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void expectLayouts(int count) {
1352d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch = new CountDownLatch(count);
1353d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1354d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1355d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void waitForLayout(long timeout) throws InterruptedException {
1356d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            waitForLayout(timeout, TimeUnit.SECONDS);
1357d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1358d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
13598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
13608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public void setOrientation(int orientation) {
13618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            super.setOrientation(orientation);
13628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            mSecondaryOrientation = null;
13638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
13648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
13658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
1366504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) {
1367504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
1368504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child));
1369504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
1370504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleView(child, recycler);
1371504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
1372504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1373504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
1374504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {
1375504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            if (DEBUG) {
1376504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index)));
1377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
1378504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            super.removeAndRecycleViewAt(index, recycler);
1379504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
1380504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1381504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
138294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar        void ensureLayoutState() {
138394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar            super.ensureLayoutState();
13848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (mSecondaryOrientation == null) {
138594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                mSecondaryOrientation = OrientationHelper.createOrientationHelper(this,
138694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar                        1 - getOrientation());
13878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
13888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
13898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1390d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {
13916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit);
1392d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            assertEquals("all expected layouts should be executed at the expected time",
1393d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    0, layoutLatch.getCount());
13946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            getInstrumentation().waitForIdleSync();
1395d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1396d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1397888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        @Override
1398888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        LayoutState createLayoutState() {
1399888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            return new LayoutState() {
1400888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                @Override
1401888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                View next(RecyclerView.Recycler recycler) {
1402888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final boolean hadMore = hasMore(mRecyclerView.mState);
1403888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final int position = mCurrentPosition;
1404888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    View next = super.next(recycler);
1405888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("if has more, should return a view", hadMore, next != null);
1406888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("position of the returned view must match current position",
1407888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
1408888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    return next;
1409888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                }
1410888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            };
1411888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        }
1412888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
1413d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String getBoundsLog() {
1414d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            StringBuilder sb = new StringBuilder();
1415d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
1416d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding());
1417d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            sb.append("\nchildren bounds\n");
1418d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int childCount = getChildCount();
1419d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
1420d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
1421d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
1422d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append("[").append("start:").append(
1423d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        mOrientationHelper.getDecoratedStart(child)).append(", end:")
1424d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n");
1425d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
1426d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return sb.toString();
1427d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1428d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1429310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException {
1430310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
1431310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            if (itemAnimator == null) {
1432310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                return;
1433310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
1434310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            final CountDownLatch latch = new CountDownLatch(1);
1435310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            final boolean running = itemAnimator.isRunning(
1436310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
1437310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        @Override
1438310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        public void onAnimationsFinished() {
1439310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                            latch.countDown();
1440310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                        }
1441310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                    }
1442310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            );
1443310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            if (running) {
1444310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                latch.await(timeoutInSeconds, TimeUnit.SECONDS);
1445310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            }
1446310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        }
1447310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
1448d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public VisibleChildren traverseAndFindVisibleChildren() {
1449d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            int childCount = getChildCount();
1450d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final VisibleChildren visibleChildren = new VisibleChildren();
1451d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int start = mOrientationHelper.getStartAfterPadding();
1452d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            final int end = mOrientationHelper.getEndAfterPadding();
1453d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            for (int i = 0; i < childCount; i++) {
1454d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                View child = getChildAt(i);
1455d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childStart = mOrientationHelper.getDecoratedStart(child);
1456d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
1457d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean fullyVisible = childStart >= start && childEnd <= end;
1458d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final boolean hidden = childEnd <= start || childStart >= end;
1459d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (hidden) {
1460d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    continue;
1461d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1462d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                final int position = getPosition(child);
1463d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (fullyVisible) {
1464d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position < visibleChildren.firstFullyVisiblePosition ||
1465d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {
1466d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstFullyVisiblePosition = position;
1467d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
1468d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1469d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    if (position > visibleChildren.lastFullyVisiblePosition) {
1470d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.lastFullyVisiblePosition = position;
1471d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    }
1472d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1473d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1474d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position < visibleChildren.firstVisiblePosition ||
1475d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {
1476d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.firstVisiblePosition = position;
1477d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1478d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1479d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                if (position > visibleChildren.lastVisiblePosition) {
1480d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    visibleChildren.lastVisiblePosition = position;
1481d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                }
1482d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1483d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            }
1484d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return visibleChildren;
1485d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1486d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
14878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Rect getViewBounds(View view) {
14888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (getOrientation() == HORIZONTAL) {
14898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
14908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
14918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
14928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view),
14938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view));
14948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            } else {
14958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                return new Rect(
14968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedStart(view),
14978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedStart(view),
14988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mSecondaryOrientation.getDecoratedEnd(view),
14998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        mOrientationHelper.getDecoratedEnd(view));
15008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
15018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
15028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
15038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
15048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        Map<Item, Rect> collectChildCoordinates() throws Throwable {
15058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
15068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            runTestOnUiThread(new Runnable() {
15078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                @Override
15088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                public void run() {
15098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    final int childCount = getChildCount();
1510cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                    Rect layoutBounds = new Rect(0, 0,
1511cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                            mLayoutManager.getWidth(), mLayoutManager.getHeight());
15128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    for (int i = 0; i < childCount; i++) {
15138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        View child = getChildAt(i);
15148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child
15158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                                .getLayoutParams();
15168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
1517cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                        Rect childBounds = getViewBounds(child);
1518cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                        if (new Rect(childBounds).intersect(layoutBounds)) {
1519cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                            items.put(vh.mBoundItem, childBounds);
1520cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                        }
15218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                    }
15228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                }
15238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            });
15248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return items;
15258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
15268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1527d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
1528d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
15296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            try {
15306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
15316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.before(recycler, state);
15326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
15336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                super.onLayoutChildren(recycler, state);
15346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                if (mOnLayoutListener != null) {
15356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                    mOnLayoutListener.after(recycler, state);
15366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                }
15376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            } catch (Throwable t) {
15386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                postExceptionToInstrumentation(t);
15396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            }
1540d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            layoutLatch.countDown();
1541d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
15426e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
15436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
15446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
15456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
15466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    static class OnLayoutListener {
15476e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void before(RecyclerView.Recycler recycler, RecyclerView.State state){}
15486e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        void after(RecyclerView.Recycler recycler, RecyclerView.State state){}
1549d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
1550d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
15518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    static class Config implements Cloneable {
1552d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
15538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private static final int DEFAULT_ITEM_COUNT = 100;
1554d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1555d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        private boolean mStackFromEnd;
1556d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1557310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int mOrientation = VERTICAL;
1558d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1559d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        boolean mReverseLayout = false;
1560d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
156149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        boolean mRecycleChildrenOnDetach = false;
156249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
1563d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        int mItemCount = DEFAULT_ITEM_COUNT;
1564d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1565504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        TestAdapter mTestAdapter;
1566504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1567d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {
1568d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
1569d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
1570d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromEnd;
1571d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1572d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1573d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config() {
1574d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1575d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1576d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1577504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        Config adapter(TestAdapter adapter) {
1578504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            mTestAdapter = adapter;
1579504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            return this;
1580504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
1581504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
158249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
158349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            mRecycleChildrenOnDetach = recycleChildrenOnDetach;
158449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            return this;
158549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        }
158649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
1587d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config orientation(int orientation) {
1588d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mOrientation = orientation;
1589d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1590d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1591d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1592d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config stackFromBottom(boolean stackFromBottom) {
1593d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mStackFromEnd = stackFromBottom;
1594d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1595d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1596d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1597d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        Config reverseLayout(boolean reverseLayout) {
1598d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mReverseLayout = reverseLayout;
1599d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1600d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1601d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
1602d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public Config itemCount(int itemCount) {
1603d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            mItemCount = itemCount;
1604d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return this;
1605d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1606d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
16078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        // required by convention
16088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        @Override
16098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        public Object clone() throws CloneNotSupportedException {
16108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            return super.clone();
16118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
16128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
1613d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        @Override
1614d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        public String toString() {
1615d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar            return "Config{" +
1616d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    "mStackFromEnd=" + mStackFromEnd +
1617d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mOrientation=" + mOrientation +
1618d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mReverseLayout=" + mReverseLayout +
161949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                    ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach +
1620d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    ", mItemCount=" + mItemCount +
1621d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar                    '}';
1622d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar        }
1623d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar    }
1624542f1260934df280985294eaef1ec8469863281fYigit Boyar
1625e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    private interface SelectTargetChildren {
1626e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        int[] selectTargetChildren(int childCount);
1627542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
1628d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar}
1629