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;
20a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat;
21a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
22061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.util.Log;
23061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.view.View;
24061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.view.ViewGroup;
25061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
26061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.ArrayList;
27061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.Arrays;
28061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.BitSet;
29afa0494a97687b705feb3659385578f33f697ea9Yigit Boyarimport java.util.HashMap;
30061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.HashSet;
31061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.List;
32afa0494a97687b705feb3659385578f33f697ea9Yigit Boyarimport java.util.Map;
33061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.Set;
34061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.concurrent.CountDownLatch;
35061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
36061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
37061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
38061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static java.util.concurrent.TimeUnit.SECONDS;
39061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
4073304eff156157f62075215e795e774803a6f96aYigit Boyarpublic class GridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
41061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
42061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    static final String TAG = "GridLayoutManagerTest";
43061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
44061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    static final boolean DEBUG = false;
45061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
46061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    WrappedGridLayoutManager mGlm;
47061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
48061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    GridTestAdapter mAdapter;
49061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
50061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    final List<Config> mBaseVariations = new ArrayList<Config>();
51061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
52061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
53061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    protected void setUp() throws Exception {
54061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        super.setUp();
55061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
56061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
57061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (int spanCount : new int[]{1, 3, 4}) {
58061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    mBaseVariations.add(new Config(spanCount, orientation, reverseLayout));
59061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
60061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
61061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
62061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
63061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
64061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public RecyclerView setupBasic(Config config) throws Throwable {
65061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return setupBasic(config, new GridTestAdapter(config.mItemCount));
66061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
67061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
68061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public RecyclerView setupBasic(Config config, GridTestAdapter testAdapter) throws Throwable {
69061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
70061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter = testAdapter;
71061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm = new WrappedGridLayoutManager(getActivity(), config.mSpanCount, config.mOrientation,
72061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                config.mReverseLayout);
73061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.assignSpanSizeLookup(mGlm);
74061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        recyclerView.setAdapter(mAdapter);
75061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        recyclerView.setLayoutManager(mGlm);
76061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return recyclerView;
77061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
78061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
79061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void waitForFirstLayout(RecyclerView recyclerView) throws Throwable {
80061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(1);
81061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        setRecyclerView(recyclerView);
82061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
83061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
84061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
8542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void testCustomWidthInHorizontal() throws Throwable {
8642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        customSizeInScrollDirectionTest(new Config(3, HORIZONTAL, false));
8742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
8842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
8942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void testCustomHeightInVertical() throws Throwable {
9042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        customSizeInScrollDirectionTest(new Config(3, VERTICAL, false));
9142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
9242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
9342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void customSizeInScrollDirectionTest(final Config config) throws Throwable {
9442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final int[] sizePerPosition = new int[]{3, 5, 9, 21, 3, 5, 9, 6, 9, 1};
9542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final int[] expectedSizePerPosition = new int[]{9, 9, 9, 21, 3, 5, 9, 9, 9, 1};
9642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final GridTestAdapter testAdapter = new GridTestAdapter(10) {
9742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            @Override
9842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
9942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    int position) {
10042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                super.onBindViewHolder(holder, position);
10142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
10242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                if (layoutParams == null) {
10342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
10442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                            ViewGroup.LayoutParams.WRAP_CONTENT);
10542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    holder.itemView.setLayoutParams(layoutParams);
10642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                }
10742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                final int size = sizePerPosition[position];
10842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
10942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams.width = size;
11042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                } else {
11142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams.height = size;
11242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                }
11342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            }
11442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        };
11542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        testAdapter.setFullSpan(3, 5);
11642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final RecyclerView rv = setupBasic(config, testAdapter);
11742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        waitForFirstLayout(rv);
11842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
11942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        assertTrue("[test sanity] some views should be laid out", mRecyclerView.getChildCount() > 0);
12042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
12142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            View child = mRecyclerView.getChildAt(i);
12242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            final int size = config.mOrientation == HORIZONTAL ? child.getWidth()
12342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    : child.getHeight();
12442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            assertEquals("child " + i + " should have the size specified in its layout params",
12542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    expectedSizePerPosition[i], size);
12642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        }
12742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        checkForMainThreadException();
12842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
12942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
130061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testLayoutParams() throws Throwable {
131061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        layoutParamsTest(GridLayoutManager.HORIZONTAL);
132061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        removeRecyclerView();
133061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        layoutParamsTest(GridLayoutManager.VERTICAL);
134061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
135061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
136a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testHorizontalAccessibilitySpanIndices() throws Throwable {
137a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        accessibilitySpanIndicesTest(HORIZONTAL);
138a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
139a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
140a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testVerticalAccessibilitySpanIndices() throws Throwable {
141a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        accessibilitySpanIndicesTest(VERTICAL);
142a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
143a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
144a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void accessibilitySpanIndicesTest(int orientation) throws Throwable {
145a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, orientation, false));
146a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        waitForFirstLayout(recyclerView);
147a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityDelegateCompat delegateCompat = mRecyclerView
148a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCompatAccessibilityDelegate().getItemDelegate();
149a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
150a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final View chosen = recyclerView.getChildAt(recyclerView.getChildCount() - 2);
151115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final int position = recyclerView.getChildLayoutPosition(chosen);
152a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        runTestOnUiThread(new Runnable() {
153a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
154a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public void run() {
155a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                delegateCompat.onInitializeAccessibilityNodeInfo(chosen, info);
156a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
157a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        });
158a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        GridLayoutManager.SpanSizeLookup ssl = mGlm.mSpanSizeLookup;
159a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo = info
160a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCollectionItemInfo();
161a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertNotNull(itemInfo);
162a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span group position",
163a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanGroupIndex(position, mGlm.getSpanCount()),
164a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getColumnIndex() : itemInfo.getRowIndex());
165a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span index",
166a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanIndex(position, mGlm.getSpanCount()),
167a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getRowIndex() :  itemInfo.getColumnIndex());
168a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span size",
169a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanSize(position),
170a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getRowSpan() :  itemInfo.getColumnSpan());
171a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
172a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
173afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public GridLayoutManager.LayoutParams ensureGridLp(View view) {
174afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        ViewGroup.LayoutParams lp = view.getLayoutParams();
175afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        GridLayoutManager.LayoutParams glp;
176afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        if (lp instanceof GridLayoutManager.LayoutParams) {
177afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) lp;
178afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        } else if (lp == null) {
179afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) mGlm
180afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    .generateDefaultLayoutParams();
181afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            view.setLayoutParams(glp);
182afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        } else {
183afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) mGlm.generateLayoutParams(lp);
184afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            view.setLayoutParams(glp);
185afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
186afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        return glp;
187afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
188afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
189061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void layoutParamsTest(final int orientation) throws Throwable {
190061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100).orientation(orientation),
191061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                new GridTestAdapter(100) {
192061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    @Override
193061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    public void onBindViewHolder(TestViewHolder holder,
194061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            int position) {
195061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        super.onBindViewHolder(holder, position);
196afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
197061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        int val = 0;
198061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        switch (position % 5) {
199061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 0:
200061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 10;
201061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
202061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 1:
203061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 30;
204061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
205061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 2:
206061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = GridLayoutManager.LayoutParams.WRAP_CONTENT;
207061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
208061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 3:
209061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = GridLayoutManager.LayoutParams.FILL_PARENT;
210061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
211061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 4:
212061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 200;
213061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
214061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
215061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (orientation == GridLayoutManager.VERTICAL) {
216061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            glp.height = val;
217061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
218061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            glp.width = val;
219061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
220061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        holder.itemView.setLayoutParams(glp);
221061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
222061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                });
223061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
224061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final OrientationHelper helper = mGlm.mOrientationHelper;
225061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int firstRowSize = Math.max(30, getSize(mGlm.findViewByPosition(2)));
226115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
227115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(0)));
228115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
229115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(1)));
230115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
231115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(2)));
232061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(0)));
233061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(1)));
234061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(2)));
235061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
236061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int secondRowSize = Math.max(200, getSize(mGlm.findViewByPosition(3)));
237115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
238115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(3)));
239115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
240115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(4)));
241115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
242115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(5)));
243061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(3)));
244061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(4)));
245061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(5)));
246061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
247061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
248061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private int getSize(View view) {
249061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (mGlm.getOrientation() == GridLayoutManager.HORIZONTAL) {
250061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return view.getWidth();
251061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
252061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return view.getHeight();
253061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
254061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
25573304eff156157f62075215e795e774803a6f96aYigit Boyar    public void testAnchorUpdate() throws InterruptedException {
25673304eff156157f62075215e795e774803a6f96aYigit Boyar        GridLayoutManager glm = new GridLayoutManager(getActivity(), 11);
25773304eff156157f62075215e795e774803a6f96aYigit Boyar        final GridLayoutManager.SpanSizeLookup spanSizeLookup
25873304eff156157f62075215e795e774803a6f96aYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
25973304eff156157f62075215e795e774803a6f96aYigit Boyar            @Override
26073304eff156157f62075215e795e774803a6f96aYigit Boyar            public int getSpanSize(int position) {
26173304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 200) {
26273304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 100;
26373304eff156157f62075215e795e774803a6f96aYigit Boyar                }
26473304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 20) {
26573304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 2;
26673304eff156157f62075215e795e774803a6f96aYigit Boyar                }
26773304eff156157f62075215e795e774803a6f96aYigit Boyar                return 1;
26873304eff156157f62075215e795e774803a6f96aYigit Boyar            }
26973304eff156157f62075215e795e774803a6f96aYigit Boyar        };
27073304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.setSpanSizeLookup(spanSizeLookup);
27173304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 11;
27242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        RecyclerView.State state = new RecyclerView.State();
273061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        state.mItemCount = 1000;
2749ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
27573304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should keep anchor in first span", 11, glm.mAnchorInfo.mPosition);
27673304eff156157f62075215e795e774803a6f96aYigit Boyar
27773304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 13;
2789ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
27973304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 11, glm.mAnchorInfo.mPosition);
28073304eff156157f62075215e795e774803a6f96aYigit Boyar
28173304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 23;
2829ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
28373304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 21, glm.mAnchorInfo.mPosition);
28473304eff156157f62075215e795e774803a6f96aYigit Boyar
28573304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 35;
2869ace89f49ed497e649b127beb12b8a237e5d30e7Yigit Boyar        glm.onAnchorReady(state, glm.mAnchorInfo);
28773304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 31, glm.mAnchorInfo.mPosition);
28873304eff156157f62075215e795e774803a6f96aYigit Boyar    }
28973304eff156157f62075215e795e774803a6f96aYigit Boyar
29073304eff156157f62075215e795e774803a6f96aYigit Boyar    public void testSpanLookup() {
291061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        spanLookupTest(false);
292061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
293061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
294061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testSpanLookupWithCache() {
295061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        spanLookupTest(true);
296061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
297061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
298061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testSpanLookupCache() {
299061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
300061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
301061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
302061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public int getSpanSize(int position) {
303061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (position > 6) {
304061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    return 2;
305061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
306061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return 1;
307061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
308061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        };
309061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(true);
310061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child non existent", -1, ssl.findReferenceIndexFromCache(2));
311061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(4, 5);
312061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child non existent", -1, ssl.findReferenceIndexFromCache(3));
313061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        // this should not happen and if happens, it is better to return -1
314061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child itself", -1, ssl.findReferenceIndexFromCache(4));
315061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(5));
316061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(100));
317061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(6, 5);
318061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(7));
319061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(6));
320061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child itself", -1, ssl.findReferenceIndexFromCache(4));
321061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(12, 5);
322061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 12, ssl.findReferenceIndexFromCache(13));
323061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(12));
324061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(7));
325061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < 6; i++) {
326061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            ssl.getCachedSpanIndex(i, 5);
327061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
328061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
329061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 1; i < 7; i++) {
330061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            assertEquals("reference child right before " + i, i - 1,
331061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ssl.findReferenceIndexFromCache(i));
332061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
333061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before 0 ", -1, ssl.findReferenceIndexFromCache(0));
334061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
335061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
336061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void spanLookupTest(boolean enableCache) {
33773304eff156157f62075215e795e774803a6f96aYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
33873304eff156157f62075215e795e774803a6f96aYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
33973304eff156157f62075215e795e774803a6f96aYigit Boyar            @Override
34073304eff156157f62075215e795e774803a6f96aYigit Boyar            public int getSpanSize(int position) {
34173304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 200) {
34273304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 100;
34373304eff156157f62075215e795e774803a6f96aYigit Boyar                }
34473304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 6) {
34573304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 2;
34673304eff156157f62075215e795e774803a6f96aYigit Boyar                }
34773304eff156157f62075215e795e774803a6f96aYigit Boyar                return 1;
34873304eff156157f62075215e795e774803a6f96aYigit Boyar            }
34973304eff156157f62075215e795e774803a6f96aYigit Boyar        };
350061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(enableCache);
351061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(0, 5));
352061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(4, ssl.getCachedSpanIndex(4, 5));
353061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(5, 5));
354061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(1, ssl.getCachedSpanIndex(6, 5));
355061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(2, ssl.getCachedSpanIndex(7, 5));
356061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(2, ssl.getCachedSpanIndex(9, 5));
357061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(8, 5));
358061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
359061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
360a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testSpanGroupIndex() {
361a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
362a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
363a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
364a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public int getSpanSize(int position) {
365a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (position > 200) {
366a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    return 100;
367a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
368a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (position > 6) {
369a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    return 2;
370a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
371a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                return 1;
372a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
373a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        };
374a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(0, ssl.getSpanGroupIndex(0, 5));
375a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(0, ssl.getSpanGroupIndex(4, 5));
376a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(5, 5));
377a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(6, 5));
378a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(7, 5));
379a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(2, ssl.getSpanGroupIndex(9, 5));
380a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(2, ssl.getSpanGroupIndex(8, 5));
381a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
382a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
383061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testNotifyDataSetChange() throws Throwable {
384061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, 100));
385061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl = mGlm.getSpanSizeLookup();
386061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(true);
387061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(recyclerView);
388061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertTrue("some positions should be cached", ssl.mSpanIndexCache.size() > 0);
389061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final Callback callback = new Callback() {
390061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
391061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void onBeforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
392061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (!state.isPreLayout()) {
393061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    assertEquals("cache should be empty", 0, ssl.mSpanIndexCache.size());
394061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
395061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
396061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
397061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
398061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void onAfterLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
399061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (!state.isPreLayout()) {
400061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    assertTrue("some items should be cached", ssl.mSpanIndexCache.size() > 0);
401061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
402061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
403061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        };
404061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.mCallbacks.add(callback);
405061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(2);
406061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.deleteAndNotify(2, 3);
407061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
408061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
409061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
410061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
411afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public void testUnevenHeights() throws Throwable {
412afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        final Map<Integer, RecyclerView.ViewHolder> viewHolderMap =
413afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new HashMap<Integer, RecyclerView.ViewHolder>();
414afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        RecyclerView recyclerView = setupBasic(new Config(3, 3), new GridTestAdapter(3) {
415afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            @Override
416afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
417afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    int position) {
418afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                super.onBindViewHolder(holder, position);
419afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                final GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
420afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                glp.height = 50 + position * 50;
421afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                viewHolderMap.put(position, holder);
422afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            }
423afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        });
424afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        waitForFirstLayout(recyclerView);
425afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
426afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should get max height", 150,
427afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getHeight());
428afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
429afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
430afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
431afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should have measured the max height", 150,
432afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getMeasuredHeight());
433afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
434afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
435afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
436afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public void testUnevenWidths() throws Throwable {
437afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        final Map<Integer, RecyclerView.ViewHolder> viewHolderMap =
438afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new HashMap<Integer, RecyclerView.ViewHolder>();
439afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        RecyclerView recyclerView = setupBasic(new Config(3, HORIZONTAL, false),
440afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new GridTestAdapter(3) {
441afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    @Override
442afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    public void onBindViewHolder(TestViewHolder holder,
443afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                            int position) {
444afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        super.onBindViewHolder(holder, position);
445afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        final GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
446afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        glp.width = 50 + position * 50;
447afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        viewHolderMap.put(position, holder);
448afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    }
449afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                });
450afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        waitForFirstLayout(recyclerView);
451afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
452afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should get max width", 150,
453afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getWidth());
454afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
455afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
456afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
457afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should have measured the max width", 150,
458afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getMeasuredWidth());
459afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
460afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
461afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
462061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testScrollBackAndPreservePositions() throws Throwable {
463061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (Config config : mBaseVariations) {
464061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            config.mItemCount = 150;
465061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            scrollBackAndPreservePositionsTest(config);
466061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            removeRecyclerView();
467061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
468061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
469061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
470061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testCacheSpanIndices() throws Throwable {
471061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100));
472061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.mSpanSizeLookup.setSpanIndexCacheEnabled(true);
473061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
474061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        GridLayoutManager.SpanSizeLookup ssl = mGlm.mSpanSizeLookup;
475061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertTrue("cache should be filled", mGlm.mSpanSizeLookup.mSpanIndexCache.size() > 0);
476061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("item index 5 should be in span 2", 2,
477061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                getLp(mGlm.findViewByPosition(5)).getSpanIndex());
478061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(2);
479061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.mFullSpanItems.add(4);
480061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.changeAndNotify(4, 1);
481061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
482061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("item index 5 should be in span 2", 0,
483061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                getLp(mGlm.findViewByPosition(5)).getSpanIndex());
484061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
485061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
486061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    GridLayoutManager.LayoutParams getLp(View view) {
487061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return (GridLayoutManager.LayoutParams) view.getLayoutParams();
488061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
489061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
490061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void scrollBackAndPreservePositionsTest(final Config config) throws Throwable {
491061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(config);
492061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 1; i < mAdapter.getItemCount(); i += config.mSpanCount + 2) {
493061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mAdapter.setFullSpan(i);
494061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
495061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
496061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int[] globalPositions = new int[mAdapter.getItemCount()];
497061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Arrays.fill(globalPositions, Integer.MIN_VALUE);
498061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int scrollStep = (mGlm.mOrientationHelper.getTotalSpace() / 20)
499061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                * (config.mReverseLayout ? -1 : 1);
500061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final String logPrefix = config.toString();
501061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int[] globalPos = new int[1];
502061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        runTestOnUiThread(new Runnable() {
503061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
504061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void run() {
505061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int globalScrollPosition = 0;
506061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int visited = 0;
507061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                while (visited < mAdapter.getItemCount()) {
508061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
509061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        View child = mRecyclerView.getChildAt(i);
510115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        final int pos = mRecyclerView.getChildLayoutPosition(child);
511061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (globalPositions[pos] != Integer.MIN_VALUE) {
512061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            continue;
513061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
514061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        visited++;
515061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
516061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                child.getLayoutParams();
517061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (config.mReverseLayout) {
518061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPositions[pos] = globalScrollPosition +
519061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
520061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
521061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPositions[pos] = globalScrollPosition +
522061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedStart(child);
523061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
524061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(logPrefix + " span index should match",
525061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
526061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                lp.getSpanIndex());
527061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
528061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    int scrolled = mGlm.scrollBy(scrollStep,
529061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
530061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    globalScrollPosition += scrolled;
531061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    if (scrolled == 0) {
532061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(
533061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                logPrefix + " If scroll is complete, all views should be visited",
534061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                visited, mAdapter.getItemCount());
535061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
536061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
537061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (DEBUG) {
538061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    Log.d(TAG, "done recording positions " + Arrays.toString(globalPositions));
539061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
540061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                globalPos[0] = globalScrollPosition;
541061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
542061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        });
543061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
544061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        runTestOnUiThread(new Runnable() {
545061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
546061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void run() {
547061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int globalScrollPosition = globalPos[0];
548061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                // now scroll back and make sure global positions match
549061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                BitSet shouldTest = new BitSet(mAdapter.getItemCount());
550061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                shouldTest.set(0, mAdapter.getItemCount() - 1, true);
551061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                String assertPrefix = config
552061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        + " global pos must match when scrolling in reverse for position ";
553061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int scrollAmount = Integer.MAX_VALUE;
554061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                while (!shouldTest.isEmpty() && scrollAmount != 0) {
555061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
556061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        View child = mRecyclerView.getChildAt(i);
557115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        int pos = mRecyclerView.getChildLayoutPosition(child);
558061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (!shouldTest.get(pos)) {
559061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            continue;
560061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
561061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
562061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                child.getLayoutParams();
563061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        shouldTest.clear(pos);
564061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        int globalPos;
565061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (config.mReverseLayout) {
566061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPos = globalScrollPosition +
567061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
568061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
569061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPos = globalScrollPosition +
570061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedStart(child);
571061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
572061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(assertPrefix + pos,
573061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                globalPositions[pos], globalPos);
574061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals("span index should match",
575061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
576061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                lp.getSpanIndex());
577061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
578061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    scrollAmount = mGlm.scrollBy(-scrollStep,
579061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
580061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    globalScrollPosition += scrollAmount;
581061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
582061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                assertTrue("all views should be seen", shouldTest.isEmpty());
583061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
584061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        });
585061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
586061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
587061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
588061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class WrappedGridLayoutManager extends GridLayoutManager {
589061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
590061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        CountDownLatch mLayoutLatch;
591061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
592061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        List<Callback> mCallbacks = new ArrayList<Callback>();
593061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
594061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public WrappedGridLayoutManager(Context context, int spanCount) {
595061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(context, spanCount);
596061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
597061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
598061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public WrappedGridLayoutManager(Context context, int spanCount, int orientation,
599061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                boolean reverseLayout) {
600061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(context, spanCount, orientation, reverseLayout);
601061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
602061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
603061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        @Override
604061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
605061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            try {
606061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (Callback callback : mCallbacks) {
607061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    callback.onBeforeLayout(recycler, state);
608061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
609061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                super.onLayoutChildren(recycler, state);
610061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (Callback callback : mCallbacks) {
611061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    callback.onAfterLayout(recycler, state);
612061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
613061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            } catch (Throwable t) {
614061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                postExceptionToInstrumentation(t);
615061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
616061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch.countDown();
617061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
618061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
619061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void expectLayout(int layoutCount) {
620061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch = new CountDownLatch(layoutCount);
621061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
622061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
623061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void waitForLayout(int seconds) throws InterruptedException {
624061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch.await(seconds, SECONDS);
625061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
626061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
627061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
628061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class Config {
629061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
630061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mSpanCount;
631061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mOrientation = GridLayoutManager.VERTICAL;
632061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mItemCount = 1000;
633061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        boolean mReverseLayout = false;
634061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
635061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Config(int spanCount, int itemCount) {
636061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanCount = spanCount;
637061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mItemCount = itemCount;
638061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
639061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
640061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public Config(int spanCount, int orientation, boolean reverseLayout) {
641061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanCount = spanCount;
642061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mOrientation = orientation;
643061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mReverseLayout = reverseLayout;
644061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
645061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
646061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Config orientation(int orientation) {
647061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mOrientation = orientation;
648061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return this;
649061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
650061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
651061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        @Override
652061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public String toString() {
653061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return "Config{" +
654061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    "mSpanCount=" + mSpanCount +
655061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mOrientation=" + (mOrientation == GridLayoutManager.HORIZONTAL ? "h" : "v") +
656061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mItemCount=" + mItemCount +
657061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mReverseLayout=" + mReverseLayout +
658061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    '}';
659061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
660061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
661061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
662061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class GridTestAdapter extends TestAdapter {
663061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
664061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Set<Integer> mFullSpanItems = new HashSet<Integer>();
665061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
666061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        GridTestAdapter(int count) {
667061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(count);
668061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
669061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
670061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        void setFullSpan(int... items) {
671061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (int i : items) {
672061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                mFullSpanItems.add(i);
673061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
674061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
675061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
676061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        void assignSpanSizeLookup(final GridLayoutManager glm) {
677061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
678061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                @Override
679061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                public int getSpanSize(int position) {
680061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    return mFullSpanItems.contains(position) ? glm.getSpanCount() : 1;
681061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
682061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            });
683061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
684061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
685061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
686061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class Callback {
687061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
688061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onBeforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
689061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
690061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
691061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onAfterLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
692061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
69373304eff156157f62075215e795e774803a6f96aYigit Boyar    }
69473304eff156157f62075215e795e774803a6f96aYigit Boyar}
695