GridLayoutManagerTest.java revision 888093b0a071a99d65c01116cf703d46b21f9918
173304eff156157f62075215e795e774803a6f96aYigit Boyar/*
273304eff156157f62075215e795e774803a6f96aYigit Boyar * Copyright (C) 2014 The Android Open Source Project
373304eff156157f62075215e795e774803a6f96aYigit Boyar *
473304eff156157f62075215e795e774803a6f96aYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
573304eff156157f62075215e795e774803a6f96aYigit Boyar * you may not use this file except in compliance with the License.
673304eff156157f62075215e795e774803a6f96aYigit Boyar * You may obtain a copy of the License at
773304eff156157f62075215e795e774803a6f96aYigit Boyar *
873304eff156157f62075215e795e774803a6f96aYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
973304eff156157f62075215e795e774803a6f96aYigit Boyar *
1073304eff156157f62075215e795e774803a6f96aYigit Boyar * Unless required by applicable law or agreed to in writing, software
1173304eff156157f62075215e795e774803a6f96aYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
1273304eff156157f62075215e795e774803a6f96aYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1373304eff156157f62075215e795e774803a6f96aYigit Boyar * See the License for the specific language governing permissions and
1473304eff156157f62075215e795e774803a6f96aYigit Boyar * limitations under the License.
1573304eff156157f62075215e795e774803a6f96aYigit Boyar */
1673304eff156157f62075215e795e774803a6f96aYigit Boyar
1773304eff156157f62075215e795e774803a6f96aYigit Boyarpackage android.support.v7.widget;
1873304eff156157f62075215e795e774803a6f96aYigit Boyar
19061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.content.Context;
201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyarimport android.os.Debug;
21a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat;
22a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
23061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.util.Log;
24888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyarimport android.util.SparseIntArray;
25061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.view.View;
26061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.view.ViewGroup;
27061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
28061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.ArrayList;
29061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.Arrays;
30061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.BitSet;
31afa0494a97687b705feb3659385578f33f697ea9Yigit Boyarimport java.util.HashMap;
32061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.HashSet;
33061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.List;
34afa0494a97687b705feb3659385578f33f697ea9Yigit Boyarimport java.util.Map;
35061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.Set;
36061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.concurrent.CountDownLatch;
37888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyarimport java.util.concurrent.atomic.AtomicBoolean;
38061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
39061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
40061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
41061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static java.util.concurrent.TimeUnit.SECONDS;
42061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
4373304eff156157f62075215e795e774803a6f96aYigit Boyarpublic class GridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
44061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
45061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    static final String TAG = "GridLayoutManagerTest";
46061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
47061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    static final boolean DEBUG = false;
48061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
49061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    WrappedGridLayoutManager mGlm;
50061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
51061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    GridTestAdapter mAdapter;
52061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
53061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    final List<Config> mBaseVariations = new ArrayList<Config>();
54061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
55061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
56061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    protected void setUp() throws Exception {
57061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        super.setUp();
58061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
59061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
60061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (int spanCount : new int[]{1, 3, 4}) {
61061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    mBaseVariations.add(new Config(spanCount, orientation, reverseLayout));
62061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
63061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
64061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
65061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
66061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
67061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public RecyclerView setupBasic(Config config) throws Throwable {
68061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return setupBasic(config, new GridTestAdapter(config.mItemCount));
69061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
70061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
71061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public RecyclerView setupBasic(Config config, GridTestAdapter testAdapter) throws Throwable {
72061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
73061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter = testAdapter;
74061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm = new WrappedGridLayoutManager(getActivity(), config.mSpanCount, config.mOrientation,
75061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                config.mReverseLayout);
76061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.assignSpanSizeLookup(mGlm);
77061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        recyclerView.setAdapter(mAdapter);
78061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        recyclerView.setLayoutManager(mGlm);
79061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return recyclerView;
80061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
81061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
82061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void waitForFirstLayout(RecyclerView recyclerView) throws Throwable {
83061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(1);
84061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        setRecyclerView(recyclerView);
85061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
86061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
87061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
8842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void testCustomWidthInHorizontal() throws Throwable {
8942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        customSizeInScrollDirectionTest(new Config(3, HORIZONTAL, false));
9042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
9142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
9242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void testCustomHeightInVertical() throws Throwable {
9342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        customSizeInScrollDirectionTest(new Config(3, VERTICAL, false));
9442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
9542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
9642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void customSizeInScrollDirectionTest(final Config config) throws Throwable {
9742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final int[] sizePerPosition = new int[]{3, 5, 9, 21, 3, 5, 9, 6, 9, 1};
9842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final int[] expectedSizePerPosition = new int[]{9, 9, 9, 21, 3, 5, 9, 9, 9, 1};
9942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final GridTestAdapter testAdapter = new GridTestAdapter(10) {
10042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            @Override
10142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
10242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    int position) {
10342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                super.onBindViewHolder(holder, position);
10442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
10542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                if (layoutParams == null) {
10642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
10742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                            ViewGroup.LayoutParams.WRAP_CONTENT);
10842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    holder.itemView.setLayoutParams(layoutParams);
10942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                }
11042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                final int size = sizePerPosition[position];
11142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
11242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams.width = size;
11342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                } else {
11442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams.height = size;
11542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                }
11642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            }
11742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        };
11842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        testAdapter.setFullSpan(3, 5);
11942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final RecyclerView rv = setupBasic(config, testAdapter);
12042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        waitForFirstLayout(rv);
12142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
12242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        assertTrue("[test sanity] some views should be laid out", mRecyclerView.getChildCount() > 0);
12342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
12442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            View child = mRecyclerView.getChildAt(i);
12542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            final int size = config.mOrientation == HORIZONTAL ? child.getWidth()
12642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    : child.getHeight();
12742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            assertEquals("child " + i + " should have the size specified in its layout params",
12842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    expectedSizePerPosition[i], size);
12942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        }
13042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        checkForMainThreadException();
13142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
13242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
133871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    public void testRTL() throws Throwable {
134871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        for (boolean changeRtlAfter : new boolean[]{false, true}) {
135871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            for (boolean oneLine : new boolean[]{false, true}) {
136871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                for (Config config : mBaseVariations) {
137871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    rtlTest(config, changeRtlAfter, oneLine);
138871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    removeRecyclerView();
139871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                }
140871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            }
141871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
142871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    }
143871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
144871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    void rtlTest(Config config, boolean changeRtlAfter, boolean oneLine) throws Throwable {
145871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (oneLine && config.mOrientation != VERTICAL) {
146871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            return;// nothing to test
147871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
148871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (config.mSpanCount == 1) {
149871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            config.mSpanCount = 2;
150871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
151871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        String logPrefix = config + ", changeRtlAfterLayout:" + changeRtlAfter + ", oneLine:" + oneLine;
152871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        config.mItemCount = 5;
153871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (oneLine) {
154871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            config.mSpanCount = config.mItemCount + 1;
155871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        } else {
156871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            config.mSpanCount = Math.min(config.mItemCount - 1, config.mSpanCount);
157871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
158871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
159871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        RecyclerView rv = setupBasic(config);
160871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (changeRtlAfter) {
161871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            waitForFirstLayout(rv);
162871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.expectLayout(1);
163871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.setFakeRtl(true);
164871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.waitForLayout(2);
165871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        } else {
166871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.mFakeRTL = true;
167871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            waitForFirstLayout(rv);
168871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
169871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
170871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        assertEquals("view should become rtl", true, mGlm.isLayoutRTL());
171871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        OrientationHelper helper = OrientationHelper.createHorizontalHelper(mGlm);
172871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        View child0 = mGlm.findViewByPosition(0);
173871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        final int secondChildPos = config.mOrientation == VERTICAL ? 1
174871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                : config.mSpanCount;
175871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        View child1 = mGlm.findViewByPosition(secondChildPos);
176871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        assertNotNull(logPrefix + " child position 0 should be laid out", child0);
177871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        assertNotNull(
178871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                logPrefix + " second child position " + (secondChildPos) + " should be laid out",
179871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                child1);
180871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (config.mOrientation == VERTICAL || !config.mReverseLayout) {
181871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertTrue(logPrefix + " second child should be to the left of first child",
182871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedStart(child0) >= helper.getDecoratedEnd(child1));
183871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertEquals(logPrefix + " first child should be right aligned",
184871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedEnd(child0), helper.getEndAfterPadding());
185871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        } else {
186871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertTrue(logPrefix + " first child should be to the left of second child",
187871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedStart(child1) >= helper.getDecoratedEnd(child0));
188871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertEquals(logPrefix + " first child should be left aligned",
189871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedStart(child0), helper.getStartAfterPadding());
190871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
191871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        checkForMainThreadException();
192871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    }
193871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
194888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar    public void testMovingAGroupOffScreenForAddedItems() throws Throwable {
195888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100));
196888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final int[] maxId = new int[1];
197888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        maxId[0] = -1;
198888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final SparseIntArray spanLookups = new SparseIntArray();
199888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final AtomicBoolean enableSpanLookupLogging = new AtomicBoolean(false);
200888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
201888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            @Override
202888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            public int getSpanSize(int position) {
203888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                if (maxId[0] > 0 && mAdapter.getItemAt(position).mId > maxId[0]) {
204888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    return 1;
205888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                } else if (enableSpanLookupLogging.get() && !rv.mState.isPreLayout()) {
206888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    spanLookups.put(position, spanLookups.get(position, 0) + 1);
207888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                }
208888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                return 3;
209888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            }
210888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        });
211888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        rv.getItemAnimator().setSupportsChangeAnimations(true);
212888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        waitForFirstLayout(rv);
213888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        View lastView = rv.getChildAt(rv.getChildCount() - 1);
214888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final int lastPos = rv.getChildAdapterPosition(lastView);
215888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        maxId[0] = mAdapter.getItemAt(mAdapter.getItemCount() - 1).mId;
216888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        // now add a lot of items below this and those new views should have span size 3
217888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        enableSpanLookupLogging.set(true);
218888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mGlm.expectLayout(2);
219888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mAdapter.addAndNotify(lastPos - 2, 30);
220888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mGlm.waitForLayout(2);
221888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        checkForMainThreadException();
222888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
223888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        assertEquals("last items span count should be queried twice", 2,
224888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                spanLookups.get(lastPos + 30));
225888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
226888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar    }
227888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
2287e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    public void testCachedBorders() throws Throwable {
2297e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        List<Config> testConfigurations = new ArrayList<Config>(mBaseVariations);
2307e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        testConfigurations.addAll(cachedBordersTestConfigs());
2317e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        for (Config config : testConfigurations) {
2327e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            gridCachedBorderstTest(config);
2337e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
2347e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
2357e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
2367e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    private void gridCachedBorderstTest(Config config) throws Throwable {
2377e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        RecyclerView recyclerView = setupBasic(config);
2387e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        waitForFirstLayout(recyclerView);
2397e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final boolean vertical = config.mOrientation == GridLayoutManager.VERTICAL;
2407e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int expectedSizeSum = vertical ? recyclerView.getWidth() : recyclerView.getHeight();
2417e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int lastVisible = mGlm.findLastVisibleItemPosition();
2427e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        for (int i = 0; i < lastVisible; i += config.mSpanCount) {
2437e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            if ((i+1)*config.mSpanCount - 1 < lastVisible) {
2447e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                int childrenSizeSum = 0;
2457e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                for (int j = 0; j < config.mSpanCount; j++) {
2467e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    View child = recyclerView.getChildAt(i * config.mSpanCount + j);
2477e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    childrenSizeSum += vertical ? child.getWidth() : child.getHeight();
2487e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                }
2497e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                assertEquals(expectedSizeSum, childrenSizeSum);
2507e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            }
2517e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
2527e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        removeRecyclerView();
2537e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
2547e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
2557e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    private List<Config> cachedBordersTestConfigs() {
2567e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        ArrayList<Config> configs = new ArrayList<Config>();
2577e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int [] spanCounts = new int[]{88, 279, 741};
2587e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int [] spanPerItem = new int[]{11, 9, 13};
2597e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
2607e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            for (boolean reverseLayout : new boolean[]{false, true}) {
2617e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                for (int i = 0 ; i < spanCounts.length; i++) {
2627e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    Config config = new Config(spanCounts[i], orientation, reverseLayout);
2637e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    config.mSpanPerItem = spanPerItem[i];
2647e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    configs.add(config);
2657e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                }
2667e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            }
2677e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
2687e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        return configs;
2697e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
2707e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
271061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testLayoutParams() throws Throwable {
272061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        layoutParamsTest(GridLayoutManager.HORIZONTAL);
273061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        removeRecyclerView();
274061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        layoutParamsTest(GridLayoutManager.VERTICAL);
275061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
276061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
277a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testHorizontalAccessibilitySpanIndices() throws Throwable {
278a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        accessibilitySpanIndicesTest(HORIZONTAL);
279a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
280a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
281a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testVerticalAccessibilitySpanIndices() throws Throwable {
282a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        accessibilitySpanIndicesTest(VERTICAL);
283a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
284a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
285a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void accessibilitySpanIndicesTest(int orientation) throws Throwable {
286a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, orientation, false));
287a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        waitForFirstLayout(recyclerView);
288a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityDelegateCompat delegateCompat = mRecyclerView
289a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCompatAccessibilityDelegate().getItemDelegate();
290a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
291a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final View chosen = recyclerView.getChildAt(recyclerView.getChildCount() - 2);
292115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final int position = recyclerView.getChildLayoutPosition(chosen);
293a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        runTestOnUiThread(new Runnable() {
294a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
295a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public void run() {
296a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                delegateCompat.onInitializeAccessibilityNodeInfo(chosen, info);
297a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
298a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        });
299a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        GridLayoutManager.SpanSizeLookup ssl = mGlm.mSpanSizeLookup;
300a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo = info
301a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCollectionItemInfo();
302a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertNotNull(itemInfo);
303a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span group position",
304a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanGroupIndex(position, mGlm.getSpanCount()),
305a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getColumnIndex() : itemInfo.getRowIndex());
306a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span index",
307a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanIndex(position, mGlm.getSpanCount()),
308a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getRowIndex() :  itemInfo.getColumnIndex());
309a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span size",
310a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanSize(position),
311a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getRowSpan() :  itemInfo.getColumnSpan());
312a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
313a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
314afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public GridLayoutManager.LayoutParams ensureGridLp(View view) {
315afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        ViewGroup.LayoutParams lp = view.getLayoutParams();
316afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        GridLayoutManager.LayoutParams glp;
317afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        if (lp instanceof GridLayoutManager.LayoutParams) {
318afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) lp;
319afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        } else if (lp == null) {
320afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) mGlm
321afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    .generateDefaultLayoutParams();
322afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            view.setLayoutParams(glp);
323afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        } else {
324afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) mGlm.generateLayoutParams(lp);
325afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            view.setLayoutParams(glp);
326afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
327afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        return glp;
328afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
329afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
330061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void layoutParamsTest(final int orientation) throws Throwable {
331061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100).orientation(orientation),
332061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                new GridTestAdapter(100) {
333061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    @Override
334061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    public void onBindViewHolder(TestViewHolder holder,
335061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            int position) {
336061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        super.onBindViewHolder(holder, position);
337afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
338061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        int val = 0;
339061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        switch (position % 5) {
340061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 0:
341061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 10;
342061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
343061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 1:
344061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 30;
345061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
346061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 2:
347061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = GridLayoutManager.LayoutParams.WRAP_CONTENT;
348061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
349061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 3:
350061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = GridLayoutManager.LayoutParams.FILL_PARENT;
351061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
352061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 4:
353061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 200;
354061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
355061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
356061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (orientation == GridLayoutManager.VERTICAL) {
357061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            glp.height = val;
358061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
359061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            glp.width = val;
360061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
361061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        holder.itemView.setLayoutParams(glp);
362061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
363061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                });
364061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
365061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final OrientationHelper helper = mGlm.mOrientationHelper;
366061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int firstRowSize = Math.max(30, getSize(mGlm.findViewByPosition(2)));
367115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
368115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(0)));
369115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
370115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(1)));
371115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
372115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(2)));
373061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(0)));
374061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(1)));
375061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(2)));
376061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
377061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int secondRowSize = Math.max(200, getSize(mGlm.findViewByPosition(3)));
378115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
379115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(3)));
380115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
381115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(4)));
382115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
383115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(5)));
384061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(3)));
385061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(4)));
386061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(5)));
387061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
388061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
389061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private int getSize(View view) {
390061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (mGlm.getOrientation() == GridLayoutManager.HORIZONTAL) {
391061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return view.getWidth();
392061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
393061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return view.getHeight();
394061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
395061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
39673304eff156157f62075215e795e774803a6f96aYigit Boyar    public void testAnchorUpdate() throws InterruptedException {
39773304eff156157f62075215e795e774803a6f96aYigit Boyar        GridLayoutManager glm = new GridLayoutManager(getActivity(), 11);
39873304eff156157f62075215e795e774803a6f96aYigit Boyar        final GridLayoutManager.SpanSizeLookup spanSizeLookup
39973304eff156157f62075215e795e774803a6f96aYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
40073304eff156157f62075215e795e774803a6f96aYigit Boyar            @Override
40173304eff156157f62075215e795e774803a6f96aYigit Boyar            public int getSpanSize(int position) {
40273304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 200) {
40373304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 100;
40473304eff156157f62075215e795e774803a6f96aYigit Boyar                }
40573304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 20) {
40673304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 2;
40773304eff156157f62075215e795e774803a6f96aYigit Boyar                }
40873304eff156157f62075215e795e774803a6f96aYigit Boyar                return 1;
40973304eff156157f62075215e795e774803a6f96aYigit Boyar            }
41073304eff156157f62075215e795e774803a6f96aYigit Boyar        };
41173304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.setSpanSizeLookup(spanSizeLookup);
41273304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 11;
41342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        RecyclerView.State state = new RecyclerView.State();
414061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        state.mItemCount = 1000;
4159ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
41673304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should keep anchor in first span", 11, glm.mAnchorInfo.mPosition);
41773304eff156157f62075215e795e774803a6f96aYigit Boyar
41873304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 13;
4199ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
42073304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 11, glm.mAnchorInfo.mPosition);
42173304eff156157f62075215e795e774803a6f96aYigit Boyar
42273304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 23;
4239ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
42473304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 21, glm.mAnchorInfo.mPosition);
42573304eff156157f62075215e795e774803a6f96aYigit Boyar
42673304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 35;
4279ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
42873304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 31, glm.mAnchorInfo.mPosition);
42973304eff156157f62075215e795e774803a6f96aYigit Boyar    }
43073304eff156157f62075215e795e774803a6f96aYigit Boyar
43173304eff156157f62075215e795e774803a6f96aYigit Boyar    public void testSpanLookup() {
432061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        spanLookupTest(false);
433061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
434061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
435061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testSpanLookupWithCache() {
436061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        spanLookupTest(true);
437061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
438061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
439061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testSpanLookupCache() {
440061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
441061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
442061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
443061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public int getSpanSize(int position) {
444061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (position > 6) {
445061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    return 2;
446061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
447061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return 1;
448061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
449061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        };
450061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(true);
451061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child non existent", -1, ssl.findReferenceIndexFromCache(2));
452061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(4, 5);
453061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child non existent", -1, ssl.findReferenceIndexFromCache(3));
454061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        // this should not happen and if happens, it is better to return -1
455061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child itself", -1, ssl.findReferenceIndexFromCache(4));
456061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(5));
457061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(100));
458061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(6, 5);
459061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(7));
460061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(6));
461061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child itself", -1, ssl.findReferenceIndexFromCache(4));
462061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(12, 5);
463061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 12, ssl.findReferenceIndexFromCache(13));
464061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(12));
465061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(7));
466061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < 6; i++) {
467061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            ssl.getCachedSpanIndex(i, 5);
468061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
469061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
470061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 1; i < 7; i++) {
471061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            assertEquals("reference child right before " + i, i - 1,
472061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ssl.findReferenceIndexFromCache(i));
473061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
474061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before 0 ", -1, ssl.findReferenceIndexFromCache(0));
475061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
476061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
477061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void spanLookupTest(boolean enableCache) {
47873304eff156157f62075215e795e774803a6f96aYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
47973304eff156157f62075215e795e774803a6f96aYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
48073304eff156157f62075215e795e774803a6f96aYigit Boyar            @Override
48173304eff156157f62075215e795e774803a6f96aYigit Boyar            public int getSpanSize(int position) {
48273304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 200) {
48373304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 100;
48473304eff156157f62075215e795e774803a6f96aYigit Boyar                }
48573304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 6) {
48673304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 2;
48773304eff156157f62075215e795e774803a6f96aYigit Boyar                }
48873304eff156157f62075215e795e774803a6f96aYigit Boyar                return 1;
48973304eff156157f62075215e795e774803a6f96aYigit Boyar            }
49073304eff156157f62075215e795e774803a6f96aYigit Boyar        };
491061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(enableCache);
492061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(0, 5));
493061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(4, ssl.getCachedSpanIndex(4, 5));
494061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(5, 5));
495061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(1, ssl.getCachedSpanIndex(6, 5));
496061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(2, ssl.getCachedSpanIndex(7, 5));
497061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(2, ssl.getCachedSpanIndex(9, 5));
498061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(8, 5));
499061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
500061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
5011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItem() throws Throwable {
5021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
5031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(VERTICAL).reverseLayout(false), 100, 0);
5041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
5051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
5061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverse() throws Throwable {
5071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
5081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(VERTICAL).reverseLayout(true), 100,
5091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                0);
5101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
5111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
5121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemHorizontal() throws Throwable {
5131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
5141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(HORIZONTAL).reverseLayout(
5151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
5161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
5171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
5181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverseHorizontal() throws Throwable {
5191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
5201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(HORIZONTAL).reverseLayout(true),
5211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 0);
5221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
5231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
5241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    /**
5251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * This tests a regression where predictive animations were not working as expected when the
5261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * first item is removed and there aren't any more items to add from that direction.
5271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * First item refers to the default anchor item.
5281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     */
5291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void removeAnchorItemTest(final Config config, int adapterSize,
5301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final int removePos) throws Throwable {
5311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        GridTestAdapter adapter = new GridTestAdapter(adapterSize) {
5321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            @Override
5331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
5341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    int position) {
5351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                super.onBindViewHolder(holder, position);
5361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (!(lp instanceof ViewGroup.MarginLayoutParams)) {
5381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    lp = new ViewGroup.MarginLayoutParams(0, 0);
5391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    holder.itemView.setLayoutParams(lp);
5401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
5411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
5421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int maxSize;
5431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
5441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getWidth();
5451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = ViewGroup.MarginLayoutParams.FILL_PARENT;
5461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
5471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getHeight();
5481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = ViewGroup.MarginLayoutParams.FILL_PARENT;
5491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
5501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
5511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int desiredSize;
5521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (position == removePos) {
5531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it large
5541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 4;
5551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
5561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it small
5571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 8;
5581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
5591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
5601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = desiredSize;
5611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
5621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = desiredSize;
5631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
5641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
5651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        };
5661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        RecyclerView recyclerView = setupBasic(config, adapter);
5671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        waitForFirstLayout(recyclerView);
5681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int childCount = mGlm.getChildCount();
5691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        RecyclerView.ViewHolder toBeRemoved = null;
5701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>();
5711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < childCount; i++) {
5721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mGlm.getChildAt(i);
5731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
5741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (holder.getAdapterPosition() == removePos) {
5751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeRemoved = holder;
5761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            } else {
5771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeMoved.add(holder);
5781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
5791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
5801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertNotNull("test sanity", toBeRemoved);
5811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("test sanity", childCount - 1, toBeMoved.size());
5821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator();
5831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mRecyclerView.setItemAnimator(loggingItemAnimator);
5841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.reset();
5851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.expectRunPendingAnimationsCall(1);
5861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mGlm.expectLayout(2);
5871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        adapter.deleteAndNotify(removePos, 1);
5881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mGlm.waitForLayout(1);
5891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.waitForPendingAnimationsCall(2);
5901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("removed child should receive remove animation",
5911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mRemoveVHs.contains(toBeRemoved));
5921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder vh : toBeMoved) {
5931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("view holder should be in moved list",
5941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(vh));
5951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
5961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>();
5971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < mGlm.getChildCount(); i++) {
5981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mGlm.getChildAt(i);
5991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
6001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (toBeRemoved != holder && !toBeMoved.contains(holder)) {
6011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                newHolders.add(holder);
6021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
6031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
6041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("some new children should show up for the new space", newHolders.size() > 0);
6051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("no items should receive animate add since they are not new", 0,
6061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mAddVHs.size());
6071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder holder : newHolders) {
6081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("new holder should receive a move animation",
6091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(holder));
6101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
6111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        // for removed view, 3 for new row
6121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("control against adding too many children due to bad layout state preparation."
6131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(),
6141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                mRecyclerView.getChildCount() <= childCount + 1 + 3);
6151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
6161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
617a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testSpanGroupIndex() {
618a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
619a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
620a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
621a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public int getSpanSize(int position) {
622a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (position > 200) {
623a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    return 100;
624a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
625a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (position > 6) {
626a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    return 2;
627a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
628a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                return 1;
629a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
630a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        };
631a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(0, ssl.getSpanGroupIndex(0, 5));
632a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(0, ssl.getSpanGroupIndex(4, 5));
633a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(5, 5));
634a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(6, 5));
635a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(7, 5));
636a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(2, ssl.getSpanGroupIndex(9, 5));
637a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(2, ssl.getSpanGroupIndex(8, 5));
638a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
639a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
640061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testNotifyDataSetChange() throws Throwable {
641061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, 100));
642061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl = mGlm.getSpanSizeLookup();
643061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(true);
644061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(recyclerView);
645061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertTrue("some positions should be cached", ssl.mSpanIndexCache.size() > 0);
646061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final Callback callback = new Callback() {
647061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
648061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void onBeforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
649061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (!state.isPreLayout()) {
650061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    assertEquals("cache should be empty", 0, ssl.mSpanIndexCache.size());
651061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
652061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
653061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
654061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
655061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void onAfterLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
656061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (!state.isPreLayout()) {
657061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    assertTrue("some items should be cached", ssl.mSpanIndexCache.size() > 0);
658061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
659061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
660061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        };
661061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.mCallbacks.add(callback);
662061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(2);
663061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.deleteAndNotify(2, 3);
664061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
665061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
666061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
667061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
668afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public void testUnevenHeights() throws Throwable {
669afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        final Map<Integer, RecyclerView.ViewHolder> viewHolderMap =
670afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new HashMap<Integer, RecyclerView.ViewHolder>();
671afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        RecyclerView recyclerView = setupBasic(new Config(3, 3), new GridTestAdapter(3) {
672afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            @Override
673afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
674afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    int position) {
675afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                super.onBindViewHolder(holder, position);
676afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                final GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
677afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                glp.height = 50 + position * 50;
678afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                viewHolderMap.put(position, holder);
679afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            }
680afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        });
681afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        waitForFirstLayout(recyclerView);
682afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
683afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should get max height", 150,
684afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getHeight());
685afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
686afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
687afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
688afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should have measured the max height", 150,
689afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getMeasuredHeight());
690afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
691afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
692afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
693afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public void testUnevenWidths() throws Throwable {
694afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        final Map<Integer, RecyclerView.ViewHolder> viewHolderMap =
695afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new HashMap<Integer, RecyclerView.ViewHolder>();
696afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        RecyclerView recyclerView = setupBasic(new Config(3, HORIZONTAL, false),
697afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new GridTestAdapter(3) {
698afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    @Override
699afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    public void onBindViewHolder(TestViewHolder holder,
700afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                            int position) {
701afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        super.onBindViewHolder(holder, position);
702afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        final GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
703afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        glp.width = 50 + position * 50;
704afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        viewHolderMap.put(position, holder);
705afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    }
706afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                });
707afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        waitForFirstLayout(recyclerView);
708afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
709afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should get max width", 150,
710afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getWidth());
711afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
712afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
713afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
714afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should have measured the max width", 150,
715afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getMeasuredWidth());
716afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
717afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
718afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
719061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testScrollBackAndPreservePositions() throws Throwable {
720061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (Config config : mBaseVariations) {
721061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            config.mItemCount = 150;
722061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            scrollBackAndPreservePositionsTest(config);
723061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            removeRecyclerView();
724061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
725061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
726061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
727204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar    public void testSpanSizeChange() throws Throwable {
728204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100));
729204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        waitForFirstLayout(rv);
730204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        assertTrue(mGlm.supportsPredictiveItemAnimations());
731204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.expectLayout(1);
732204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        runTestOnUiThread(new Runnable() {
733204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar            @Override
734204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar            public void run() {
735204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar                mGlm.setSpanCount(5);
736204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar                assertFalse(mGlm.supportsPredictiveItemAnimations());
737204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar            }
738204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        });
739204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        checkForMainThreadException();
740204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.waitForLayout(2);
741204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.expectLayout(2);
742204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mAdapter.deleteAndNotify(3, 2);
743204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.waitForLayout(2);
744204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        assertTrue(mGlm.supportsPredictiveItemAnimations());
745204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar    }
746204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar
747061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testCacheSpanIndices() throws Throwable {
748061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100));
749061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.mSpanSizeLookup.setSpanIndexCacheEnabled(true);
750061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
751061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        GridLayoutManager.SpanSizeLookup ssl = mGlm.mSpanSizeLookup;
752061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertTrue("cache should be filled", mGlm.mSpanSizeLookup.mSpanIndexCache.size() > 0);
753061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("item index 5 should be in span 2", 2,
754061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                getLp(mGlm.findViewByPosition(5)).getSpanIndex());
755061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(2);
756061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.mFullSpanItems.add(4);
757061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.changeAndNotify(4, 1);
758061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
759061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("item index 5 should be in span 2", 0,
760061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                getLp(mGlm.findViewByPosition(5)).getSpanIndex());
761061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
762061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
763061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    GridLayoutManager.LayoutParams getLp(View view) {
764061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return (GridLayoutManager.LayoutParams) view.getLayoutParams();
765061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
766061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
767061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void scrollBackAndPreservePositionsTest(final Config config) throws Throwable {
768061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(config);
769061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 1; i < mAdapter.getItemCount(); i += config.mSpanCount + 2) {
770061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mAdapter.setFullSpan(i);
771061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
772061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
773061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int[] globalPositions = new int[mAdapter.getItemCount()];
774061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Arrays.fill(globalPositions, Integer.MIN_VALUE);
775061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int scrollStep = (mGlm.mOrientationHelper.getTotalSpace() / 20)
776061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                * (config.mReverseLayout ? -1 : 1);
777061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final String logPrefix = config.toString();
778061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int[] globalPos = new int[1];
779061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        runTestOnUiThread(new Runnable() {
780061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
781061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void run() {
7821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                assertSame("test sanity", mRecyclerView, rv);
783061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int globalScrollPosition = 0;
784061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int visited = 0;
785061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                while (visited < mAdapter.getItemCount()) {
786061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
787061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        View child = mRecyclerView.getChildAt(i);
788115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        final int pos = mRecyclerView.getChildLayoutPosition(child);
789061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (globalPositions[pos] != Integer.MIN_VALUE) {
790061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            continue;
791061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
792061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        visited++;
793061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
794061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                child.getLayoutParams();
795061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (config.mReverseLayout) {
796061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPositions[pos] = globalScrollPosition +
797061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
798061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
799061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPositions[pos] = globalScrollPosition +
800061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedStart(child);
801061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
802061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(logPrefix + " span index should match",
803061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
804061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                lp.getSpanIndex());
805061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
806061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    int scrolled = mGlm.scrollBy(scrollStep,
807061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
808061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    globalScrollPosition += scrolled;
809061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    if (scrolled == 0) {
810061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(
811061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                logPrefix + " If scroll is complete, all views should be visited",
812061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                visited, mAdapter.getItemCount());
813061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
814061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
815061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (DEBUG) {
816061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    Log.d(TAG, "done recording positions " + Arrays.toString(globalPositions));
817061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
818061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                globalPos[0] = globalScrollPosition;
819061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
820061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        });
821061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
8221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        // test sanity, ensure scroll happened
8231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        runTestOnUiThread(new Runnable() {
8241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            @Override
8251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            public void run() {
8261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int childCount = mGlm.getChildCount();
8271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final BitSet expectedPositions = new BitSet();
8281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                for (int i = 0; i < childCount; i ++) {
8291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    expectedPositions.set(mAdapter.getItemCount() - i - 1);
8301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
8311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                for (int i = 0; i <childCount; i ++) {
8321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    final View view = mGlm.getChildAt(i);
8331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    int position = mGlm.getPosition(view);
8341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    assertTrue("child position should be in last page", expectedPositions.get(position));
8351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
8361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
8371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        });
8381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        getInstrumentation().waitForIdleSync();
839061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        runTestOnUiThread(new Runnable() {
840061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
841061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void run() {
842061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int globalScrollPosition = globalPos[0];
843061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                // now scroll back and make sure global positions match
844061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                BitSet shouldTest = new BitSet(mAdapter.getItemCount());
845061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                shouldTest.set(0, mAdapter.getItemCount() - 1, true);
846061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                String assertPrefix = config
847061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        + " global pos must match when scrolling in reverse for position ";
848061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int scrollAmount = Integer.MAX_VALUE;
849061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                while (!shouldTest.isEmpty() && scrollAmount != 0) {
850061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
851061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        View child = mRecyclerView.getChildAt(i);
852115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        int pos = mRecyclerView.getChildLayoutPosition(child);
853061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (!shouldTest.get(pos)) {
854061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            continue;
855061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
856061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
857061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                child.getLayoutParams();
858061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        shouldTest.clear(pos);
859061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        int globalPos;
860061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (config.mReverseLayout) {
861061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPos = globalScrollPosition +
862061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
863061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
864061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPos = globalScrollPosition +
865061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedStart(child);
866061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
867061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(assertPrefix + pos,
868061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                globalPositions[pos], globalPos);
869061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals("span index should match",
870061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
871061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                lp.getSpanIndex());
872061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
873061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    scrollAmount = mGlm.scrollBy(-scrollStep,
874061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
875061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    globalScrollPosition += scrollAmount;
876061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
877061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                assertTrue("all views should be seen", shouldTest.isEmpty());
878061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
879061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        });
880061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
881061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
882061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
883061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class WrappedGridLayoutManager extends GridLayoutManager {
884061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
885061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        CountDownLatch mLayoutLatch;
886061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
887061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        List<Callback> mCallbacks = new ArrayList<Callback>();
888061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
889871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        Boolean mFakeRTL;
890871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
891061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public WrappedGridLayoutManager(Context context, int spanCount) {
892061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(context, spanCount);
893061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
894061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
895061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public WrappedGridLayoutManager(Context context, int spanCount, int orientation,
896061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                boolean reverseLayout) {
897061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(context, spanCount, orientation, reverseLayout);
898061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
899061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
900061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        @Override
901871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        protected boolean isLayoutRTL() {
902871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            return mFakeRTL == null ? super.isLayoutRTL() : mFakeRTL;
903871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
904871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
905871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        public void setFakeRtl(Boolean fakeRtl) {
906871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mFakeRTL = fakeRtl;
907871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            try {
908871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                requestLayoutOnUIThread(mRecyclerView);
909871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            } catch (Throwable throwable) {
910871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                postExceptionToInstrumentation(throwable);
911871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            }
912871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
913871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
914871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        @Override
915061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
916061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            try {
917061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (Callback callback : mCallbacks) {
918061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    callback.onBeforeLayout(recycler, state);
919061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
920061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                super.onLayoutChildren(recycler, state);
921061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (Callback callback : mCallbacks) {
922061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    callback.onAfterLayout(recycler, state);
923061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
924061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            } catch (Throwable t) {
925061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                postExceptionToInstrumentation(t);
926061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
927061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch.countDown();
928061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
929061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
930888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        @Override
931888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        LayoutState createLayoutState() {
932888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            return new LayoutState() {
933888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                @Override
934888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                View next(RecyclerView.Recycler recycler) {
935888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final boolean hadMore = hasMore(mRecyclerView.mState);
936888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final int position = mCurrentPosition;
937888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    View next = super.next(recycler);
938888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("if has more, should return a view", hadMore, next != null);
939888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("position of the returned view must match current position",
940888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
941888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    return next;
942888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                }
943888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            };
944888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        }
945888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
946061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void expectLayout(int layoutCount) {
947061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch = new CountDownLatch(layoutCount);
948061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
949061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
950061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void waitForLayout(int seconds) throws InterruptedException {
951061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch.await(seconds, SECONDS);
952061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
953061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
954061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
955061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class Config {
956061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
957061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mSpanCount;
958061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mOrientation = GridLayoutManager.VERTICAL;
959061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mItemCount = 1000;
9607e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int mSpanPerItem = 1;
961061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        boolean mReverseLayout = false;
962061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
963061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Config(int spanCount, int itemCount) {
964061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanCount = spanCount;
965061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mItemCount = itemCount;
966061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
967061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
968061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public Config(int spanCount, int orientation, boolean reverseLayout) {
969061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanCount = spanCount;
970061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mOrientation = orientation;
971061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mReverseLayout = reverseLayout;
972061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
973061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
974061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Config orientation(int orientation) {
975061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mOrientation = orientation;
976061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return this;
977061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
978061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
979061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        @Override
980061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public String toString() {
981061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return "Config{" +
982061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    "mSpanCount=" + mSpanCount +
983061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mOrientation=" + (mOrientation == GridLayoutManager.HORIZONTAL ? "h" : "v") +
984061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mItemCount=" + mItemCount +
985061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mReverseLayout=" + mReverseLayout +
986061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    '}';
987061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
9881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
9891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        public Config reverseLayout(boolean reverseLayout) {
9901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            mReverseLayout = reverseLayout;
9911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            return this;
9921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
9931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
9941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
995061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
996061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
997061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class GridTestAdapter extends TestAdapter {
998061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
999061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Set<Integer> mFullSpanItems = new HashSet<Integer>();
10007e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int mSpanPerItem = 1;
1001061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1002061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        GridTestAdapter(int count) {
1003061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(count);
1004061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1005061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
10067e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        GridTestAdapter(int count, int spanPerItem) {
10077e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            super(count);
10087e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            mSpanPerItem = spanPerItem;
10097e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
10107e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
1011061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        void setFullSpan(int... items) {
1012061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (int i : items) {
1013061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                mFullSpanItems.add(i);
1014061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
1015061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1016061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1017061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        void assignSpanSizeLookup(final GridLayoutManager glm) {
1018061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
1019061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                @Override
1020061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                public int getSpanSize(int position) {
10217e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    return mFullSpanItems.contains(position) ? glm.getSpanCount() : mSpanPerItem;
1022061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
1023061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            });
1024061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1025061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
1026061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1027061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class Callback {
1028061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1029061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onBeforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
1030061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1031061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1032061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onAfterLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
1033061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
103473304eff156157f62075215e795e774803a6f96aYigit Boyar    }
103573304eff156157f62075215e795e774803a6f96aYigit Boyar}
1036