GridLayoutManagerTest.java revision bb2163aca1052da169c15625c9afca7b85491ebf
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;
203bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyarimport android.graphics.Rect;
21a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat;
22a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
23bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyarimport android.test.UiThreadTest;
24061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.util.Log;
25888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyarimport android.util.SparseIntArray;
26061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.view.View;
27061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport android.view.ViewGroup;
28061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
29061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.ArrayList;
30061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.Arrays;
31061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.BitSet;
32afa0494a97687b705feb3659385578f33f697ea9Yigit Boyarimport java.util.HashMap;
33061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.HashSet;
34061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.List;
35afa0494a97687b705feb3659385578f33f697ea9Yigit Boyarimport java.util.Map;
36061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.Set;
37061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport java.util.concurrent.CountDownLatch;
38888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyarimport java.util.concurrent.atomic.AtomicBoolean;
39061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
40061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
41061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
42061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyarimport static java.util.concurrent.TimeUnit.SECONDS;
43061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
4473304eff156157f62075215e795e774803a6f96aYigit Boyarpublic class GridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
45061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
46061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    static final String TAG = "GridLayoutManagerTest";
47061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
48061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    static final boolean DEBUG = false;
49061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
50061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    WrappedGridLayoutManager mGlm;
51061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
52061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    GridTestAdapter mAdapter;
53061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
54061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    final List<Config> mBaseVariations = new ArrayList<Config>();
55061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
56061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    @Override
57061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    protected void setUp() throws Exception {
58061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        super.setUp();
59061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
60061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (boolean reverseLayout : new boolean[]{false, true}) {
61061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (int spanCount : new int[]{1, 3, 4}) {
62061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    mBaseVariations.add(new Config(spanCount, orientation, reverseLayout));
63061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
64061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
65061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
66061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
67061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
68061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public RecyclerView setupBasic(Config config) throws Throwable {
69061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return setupBasic(config, new GridTestAdapter(config.mItemCount));
70061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
71061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
72061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public RecyclerView setupBasic(Config config, GridTestAdapter testAdapter) throws Throwable {
73061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
74061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter = testAdapter;
75061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm = new WrappedGridLayoutManager(getActivity(), config.mSpanCount, config.mOrientation,
76061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                config.mReverseLayout);
77061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.assignSpanSizeLookup(mGlm);
78061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        recyclerView.setAdapter(mAdapter);
79061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        recyclerView.setLayoutManager(mGlm);
80061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return recyclerView;
81061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
82061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
83061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void waitForFirstLayout(RecyclerView recyclerView) throws Throwable {
84061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(1);
85061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        setRecyclerView(recyclerView);
86061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
87061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
88061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
89bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    @UiThreadTest
90bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    public void testScrollWithoutLayout() throws Throwable {
91bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, 100));
92bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        mGlm.expectLayout(1);
93bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        setRecyclerView(recyclerView);
94bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        mGlm.setSpanCount(5);
95bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        recyclerView.scrollBy(0, 10);
96bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    }
97bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar
98bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    public void testScrollWithoutLayoutAfterInvalidate() throws Throwable {
99bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, 100));
100bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        waitForFirstLayout(recyclerView);
101bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        runTestOnUiThread(new Runnable() {
102bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar            @Override
103bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar            public void run() {
104bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar                mGlm.setSpanCount(5);
105bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar                recyclerView.scrollBy(0, 10);
106bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar            }
107bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar        });
108bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar    }
109bb2163aca1052da169c15625c9afca7b85491ebfYigit Boyar
110cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    public void testPredictiveSpanLookup1() throws Throwable {
111cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        predictiveSpanLookupTest(0, false);
112cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    }
113cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar
114cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    public void testPredictiveSpanLookup2() throws Throwable {
115cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        predictiveSpanLookupTest(0, true);
116cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    }
117cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar
118cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    public void testPredictiveSpanLookup3() throws Throwable {
119cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        predictiveSpanLookupTest(1, false);
120cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    }
121cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar
122cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    public void testPredictiveSpanLookup4() throws Throwable {
123cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        predictiveSpanLookupTest(1, true);
124cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    }
125cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar
126cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    public void predictiveSpanLookupTest(int remaining, boolean removeFromStart) throws Throwable {
127cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        RecyclerView recyclerView = setupBasic(new Config(3, 10));
128cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
129cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar            @Override
130cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar            public int getSpanSize(int position) {
131cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                if (position < 0 || position >= mAdapter.getItemCount()) {
132cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                    postExceptionToInstrumentation(new AssertionError("position is not within " +
133cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                            "adapter range. pos:" + position + ", adapter size:" +
134cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                            mAdapter.getItemCount()));
135cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                }
136cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                return 1;
137cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar            }
138cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar
139cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar            @Override
140cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar            public int getSpanIndex(int position, int spanCount) {
141cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                if (position < 0 || position >= mAdapter.getItemCount()) {
142cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                    postExceptionToInstrumentation(new AssertionError("position is not within " +
143cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                            "adapter range. pos:" + position + ", adapter size:" +
144cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                            mAdapter.getItemCount()));
145cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                }
146cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar                return super.getSpanIndex(position, spanCount);
147cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar            }
148cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        });
149cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        waitForFirstLayout(recyclerView);
150cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        checkForMainThreadException();
151cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        assertTrue("test sanity", mGlm.supportsPredictiveItemAnimations());
152cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        mGlm.expectLayout(2);
153cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        int deleteCnt = 10 - remaining;
154cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        int deleteStart = removeFromStart ? 0 : remaining;
155cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        mAdapter.deleteAndNotify(deleteStart, deleteCnt);
156cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        mGlm.waitForLayout(2);
157cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        checkForMainThreadException();
158cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar    }
159cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar
16042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void testCustomWidthInHorizontal() throws Throwable {
16142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        customSizeInScrollDirectionTest(new Config(3, HORIZONTAL, false));
16242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
16342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
16442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void testCustomHeightInVertical() throws Throwable {
16542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        customSizeInScrollDirectionTest(new Config(3, VERTICAL, false));
16642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
16742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
16842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    public void customSizeInScrollDirectionTest(final Config config) throws Throwable {
1693bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar        Boolean[] options = new Boolean[]{true, false};
1703bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar        for (boolean addMargins : options) {
1713bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar            for (boolean addDecorOffsets : options) {
1723bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                customSizeInScrollDirectionTest(config, addDecorOffsets, addMargins);
1733bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar            }
1743bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar        }
1753bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar    }
1763bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar
1773bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar    public void customSizeInScrollDirectionTest(final Config config, boolean addDecorOffsets,
1783bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar            boolean addMarigns) throws Throwable {
1793bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar        final int decorOffset = addDecorOffsets ? 7 : 0;
1803bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar        final int margin = addMarigns ? 11 : 0;
18142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final int[] sizePerPosition = new int[]{3, 5, 9, 21, 3, 5, 9, 6, 9, 1};
18242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final int[] expectedSizePerPosition = new int[]{9, 9, 9, 21, 3, 5, 9, 9, 9, 1};
1833bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar
18442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final GridTestAdapter testAdapter = new GridTestAdapter(10) {
18542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            @Override
18642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
18742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    int position) {
18842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                super.onBindViewHolder(holder, position);
1893bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)
1903bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                        holder.itemView.getLayoutParams();
19142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                if (layoutParams == null) {
1923bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    layoutParams = new ViewGroup.MarginLayoutParams(
1933bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                            ViewGroup.LayoutParams.WRAP_CONTENT,
19442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                            ViewGroup.LayoutParams.WRAP_CONTENT);
19542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    holder.itemView.setLayoutParams(layoutParams);
19642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                }
19742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                final int size = sizePerPosition[position];
19842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
19942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams.width = size;
2003bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    layoutParams.leftMargin = margin;
2013bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    layoutParams.rightMargin = margin;
20242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                } else {
20342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    layoutParams.height = size;
2043bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    layoutParams.topMargin = margin;
2053bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    layoutParams.bottomMargin = margin;
20642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                }
20742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            }
20842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        };
20942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        testAdapter.setFullSpan(3, 5);
21042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        final RecyclerView rv = setupBasic(config, testAdapter);
2113bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar        if (addDecorOffsets) {
2123bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar            rv.addItemDecoration(new RecyclerView.ItemDecoration() {
2133bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                @Override
2143bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
2153bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                        RecyclerView.State state) {
2163bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    if (config.mOrientation == HORIZONTAL) {
2173bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                        outRect.set(decorOffset, 0, decorOffset, 0);
2183bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    } else {
2193bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                        outRect.set(0, decorOffset, 0, decorOffset);
2203bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                    }
2213bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar                }
2223bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar            });
2233bc9692bd8ee36a1eced339564ea6eaa4c8261dbYigit Boyar        }
22442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        waitForFirstLayout(rv);
22542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
22642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        assertTrue("[test sanity] some views should be laid out", mRecyclerView.getChildCount() > 0);
22742e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
22842e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            View child = mRecyclerView.getChildAt(i);
22942e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            final int size = config.mOrientation == HORIZONTAL ? child.getWidth()
23042e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    : child.getHeight();
23142e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar            assertEquals("child " + i + " should have the size specified in its layout params",
23242e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar                    expectedSizePerPosition[i], size);
23342e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        }
23442e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar        checkForMainThreadException();
23542e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar    }
23642e9353bb9eb2747247e30e3612b227945acfd16Yigit Boyar
237871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    public void testRTL() throws Throwable {
238871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        for (boolean changeRtlAfter : new boolean[]{false, true}) {
239871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            for (boolean oneLine : new boolean[]{false, true}) {
240871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                for (Config config : mBaseVariations) {
241871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    rtlTest(config, changeRtlAfter, oneLine);
242871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    removeRecyclerView();
243871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                }
244871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            }
245871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
246871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    }
247871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
248871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    void rtlTest(Config config, boolean changeRtlAfter, boolean oneLine) throws Throwable {
249871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (oneLine && config.mOrientation != VERTICAL) {
250871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            return;// nothing to test
251871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
252871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (config.mSpanCount == 1) {
253871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            config.mSpanCount = 2;
254871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
255871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        String logPrefix = config + ", changeRtlAfterLayout:" + changeRtlAfter + ", oneLine:" + oneLine;
256871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        config.mItemCount = 5;
257871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (oneLine) {
258871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            config.mSpanCount = config.mItemCount + 1;
259871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        } else {
260871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            config.mSpanCount = Math.min(config.mItemCount - 1, config.mSpanCount);
261871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
262871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
263871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        RecyclerView rv = setupBasic(config);
264871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (changeRtlAfter) {
265871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            waitForFirstLayout(rv);
266871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.expectLayout(1);
267871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.setFakeRtl(true);
268871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.waitForLayout(2);
269871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        } else {
270871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mGlm.mFakeRTL = true;
271871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            waitForFirstLayout(rv);
272871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
273871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
274871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        assertEquals("view should become rtl", true, mGlm.isLayoutRTL());
275871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        OrientationHelper helper = OrientationHelper.createHorizontalHelper(mGlm);
276871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        View child0 = mGlm.findViewByPosition(0);
277871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        final int secondChildPos = config.mOrientation == VERTICAL ? 1
278871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                : config.mSpanCount;
279871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        View child1 = mGlm.findViewByPosition(secondChildPos);
280871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        assertNotNull(logPrefix + " child position 0 should be laid out", child0);
281871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        assertNotNull(
282871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                logPrefix + " second child position " + (secondChildPos) + " should be laid out",
283871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                child1);
284871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        if (config.mOrientation == VERTICAL || !config.mReverseLayout) {
285871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertTrue(logPrefix + " second child should be to the left of first child",
286871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedStart(child0) >= helper.getDecoratedEnd(child1));
287871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertEquals(logPrefix + " first child should be right aligned",
288871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedEnd(child0), helper.getEndAfterPadding());
289871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        } else {
290871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertTrue(logPrefix + " first child should be to the left of second child",
291871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedStart(child1) >= helper.getDecoratedEnd(child0));
292871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            assertEquals(logPrefix + " first child should be left aligned",
293871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                    helper.getDecoratedStart(child0), helper.getStartAfterPadding());
294871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
295871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        checkForMainThreadException();
296871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar    }
297871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
298888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar    public void testMovingAGroupOffScreenForAddedItems() throws Throwable {
299888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100));
300888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final int[] maxId = new int[1];
301888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        maxId[0] = -1;
302888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final SparseIntArray spanLookups = new SparseIntArray();
303888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final AtomicBoolean enableSpanLookupLogging = new AtomicBoolean(false);
304888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
305888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            @Override
306888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            public int getSpanSize(int position) {
307888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                if (maxId[0] > 0 && mAdapter.getItemAt(position).mId > maxId[0]) {
308888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    return 1;
309888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                } else if (enableSpanLookupLogging.get() && !rv.mState.isPreLayout()) {
310888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    spanLookups.put(position, spanLookups.get(position, 0) + 1);
311888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                }
312888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                return 3;
313888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            }
314888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        });
315121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        ((SimpleItemAnimator)rv.getItemAnimator()).setSupportsChangeAnimations(true);
316888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        waitForFirstLayout(rv);
317888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        View lastView = rv.getChildAt(rv.getChildCount() - 1);
318888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        final int lastPos = rv.getChildAdapterPosition(lastView);
319888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        maxId[0] = mAdapter.getItemAt(mAdapter.getItemCount() - 1).mId;
320888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        // now add a lot of items below this and those new views should have span size 3
321888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        enableSpanLookupLogging.set(true);
322888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mGlm.expectLayout(2);
323888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mAdapter.addAndNotify(lastPos - 2, 30);
324888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        mGlm.waitForLayout(2);
325888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        checkForMainThreadException();
326888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
327888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        assertEquals("last items span count should be queried twice", 2,
328888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                spanLookups.get(lastPos + 30));
329888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
330888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar    }
331888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
3327e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    public void testCachedBorders() throws Throwable {
3337e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        List<Config> testConfigurations = new ArrayList<Config>(mBaseVariations);
3347e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        testConfigurations.addAll(cachedBordersTestConfigs());
3357e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        for (Config config : testConfigurations) {
3367e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            gridCachedBorderstTest(config);
3377e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
3387e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
3397e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
3407e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    private void gridCachedBorderstTest(Config config) throws Throwable {
3417e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        RecyclerView recyclerView = setupBasic(config);
3427e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        waitForFirstLayout(recyclerView);
3437e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final boolean vertical = config.mOrientation == GridLayoutManager.VERTICAL;
3447e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int expectedSizeSum = vertical ? recyclerView.getWidth() : recyclerView.getHeight();
3457e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int lastVisible = mGlm.findLastVisibleItemPosition();
3467e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        for (int i = 0; i < lastVisible; i += config.mSpanCount) {
3477e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            if ((i+1)*config.mSpanCount - 1 < lastVisible) {
3487e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                int childrenSizeSum = 0;
3497e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                for (int j = 0; j < config.mSpanCount; j++) {
3507e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    View child = recyclerView.getChildAt(i * config.mSpanCount + j);
3517e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    childrenSizeSum += vertical ? child.getWidth() : child.getHeight();
3527e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                }
3537e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                assertEquals(expectedSizeSum, childrenSizeSum);
3547e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            }
3557e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
3567e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        removeRecyclerView();
3577e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
3587e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
3597e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    private List<Config> cachedBordersTestConfigs() {
3607e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        ArrayList<Config> configs = new ArrayList<Config>();
3617e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int [] spanCounts = new int[]{88, 279, 741};
3627e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        final int [] spanPerItem = new int[]{11, 9, 13};
3637e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
3647e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            for (boolean reverseLayout : new boolean[]{false, true}) {
3657e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                for (int i = 0 ; i < spanCounts.length; i++) {
3667e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    Config config = new Config(spanCounts[i], orientation, reverseLayout);
3677e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    config.mSpanPerItem = spanPerItem[i];
3687e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    configs.add(config);
3697e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                }
3707e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            }
3717e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
3727e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        return configs;
3737e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko    }
3747e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
375061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testLayoutParams() throws Throwable {
376061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        layoutParamsTest(GridLayoutManager.HORIZONTAL);
377061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        removeRecyclerView();
378061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        layoutParamsTest(GridLayoutManager.VERTICAL);
379061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
380061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
381a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testHorizontalAccessibilitySpanIndices() throws Throwable {
382a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        accessibilitySpanIndicesTest(HORIZONTAL);
383a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
384a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
385a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testVerticalAccessibilitySpanIndices() throws Throwable {
386a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        accessibilitySpanIndicesTest(VERTICAL);
387a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
388a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
389a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void accessibilitySpanIndicesTest(int orientation) throws Throwable {
390a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, orientation, false));
391a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        waitForFirstLayout(recyclerView);
392a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityDelegateCompat delegateCompat = mRecyclerView
393a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCompatAccessibilityDelegate().getItemDelegate();
394a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
395a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final View chosen = recyclerView.getChildAt(recyclerView.getChildCount() - 2);
396115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final int position = recyclerView.getChildLayoutPosition(chosen);
397a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        runTestOnUiThread(new Runnable() {
398a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
399a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public void run() {
400a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                delegateCompat.onInitializeAccessibilityNodeInfo(chosen, info);
401a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
402a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        });
403a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        GridLayoutManager.SpanSizeLookup ssl = mGlm.mSpanSizeLookup;
404a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo = info
405a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCollectionItemInfo();
406a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertNotNull(itemInfo);
407a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span group position",
408a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanGroupIndex(position, mGlm.getSpanCount()),
409a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getColumnIndex() : itemInfo.getRowIndex());
410a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span index",
411a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanIndex(position, mGlm.getSpanCount()),
412a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getRowIndex() :  itemInfo.getColumnIndex());
413a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have span size",
414a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                ssl.getSpanSize(position),
415a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                orientation == HORIZONTAL ? itemInfo.getRowSpan() :  itemInfo.getColumnSpan());
416a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
417a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
418afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public GridLayoutManager.LayoutParams ensureGridLp(View view) {
419afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        ViewGroup.LayoutParams lp = view.getLayoutParams();
420afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        GridLayoutManager.LayoutParams glp;
421afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        if (lp instanceof GridLayoutManager.LayoutParams) {
422afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) lp;
423afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        } else if (lp == null) {
424afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) mGlm
425afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    .generateDefaultLayoutParams();
426afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            view.setLayoutParams(glp);
427afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        } else {
428afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            glp = (GridLayoutManager.LayoutParams) mGlm.generateLayoutParams(lp);
429afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            view.setLayoutParams(glp);
430afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
431afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        return glp;
432afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
433afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
434061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void layoutParamsTest(final int orientation) throws Throwable {
435061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100).orientation(orientation),
436061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                new GridTestAdapter(100) {
437061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    @Override
438061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    public void onBindViewHolder(TestViewHolder holder,
439061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            int position) {
440061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        super.onBindViewHolder(holder, position);
441afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
442061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        int val = 0;
443061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        switch (position % 5) {
444061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 0:
445061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 10;
446061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
447061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 1:
448061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 30;
449061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
450061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 2:
451061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = GridLayoutManager.LayoutParams.WRAP_CONTENT;
452061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
453061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 3:
454061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = GridLayoutManager.LayoutParams.FILL_PARENT;
455061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
456061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            case 4:
457061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                val = 200;
458061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                break;
459061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
460061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (orientation == GridLayoutManager.VERTICAL) {
461061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            glp.height = val;
462061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
463061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            glp.width = val;
464061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
465061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        holder.itemView.setLayoutParams(glp);
466061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
467061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                });
468061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
469061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final OrientationHelper helper = mGlm.mOrientationHelper;
470061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int firstRowSize = Math.max(30, getSize(mGlm.findViewByPosition(2)));
471115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
472115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(0)));
473115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
474115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(1)));
475115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(firstRowSize,
476115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(2)));
477061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(0)));
478061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(1)));
479061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(firstRowSize, getSize(mGlm.findViewByPosition(2)));
480061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
481061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int secondRowSize = Math.max(200, getSize(mGlm.findViewByPosition(3)));
482115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
483115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(3)));
484115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
485115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(4)));
486115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        assertEquals(secondRowSize,
487115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                helper.getDecoratedMeasurement(mGlm.findViewByPosition(5)));
488061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(3)));
489061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(4)));
490061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(secondRowSize, getSize(mGlm.findViewByPosition(5)));
491061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
492061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
493061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    private int getSize(View view) {
494061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        if (mGlm.getOrientation() == GridLayoutManager.HORIZONTAL) {
495061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return view.getWidth();
496061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
497061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return view.getHeight();
498061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
499061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
50073304eff156157f62075215e795e774803a6f96aYigit Boyar    public void testAnchorUpdate() throws InterruptedException {
50173304eff156157f62075215e795e774803a6f96aYigit Boyar        GridLayoutManager glm = new GridLayoutManager(getActivity(), 11);
50273304eff156157f62075215e795e774803a6f96aYigit Boyar        final GridLayoutManager.SpanSizeLookup spanSizeLookup
50373304eff156157f62075215e795e774803a6f96aYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
50473304eff156157f62075215e795e774803a6f96aYigit Boyar            @Override
50573304eff156157f62075215e795e774803a6f96aYigit Boyar            public int getSpanSize(int position) {
50673304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 200) {
50773304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 100;
50873304eff156157f62075215e795e774803a6f96aYigit Boyar                }
50973304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 20) {
51073304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 2;
51173304eff156157f62075215e795e774803a6f96aYigit Boyar                }
51273304eff156157f62075215e795e774803a6f96aYigit Boyar                return 1;
51373304eff156157f62075215e795e774803a6f96aYigit Boyar            }
51473304eff156157f62075215e795e774803a6f96aYigit Boyar        };
51573304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.setSpanSizeLookup(spanSizeLookup);
51673304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 11;
51742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        RecyclerView.State state = new RecyclerView.State();
518cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        mRecyclerView = new RecyclerView(getActivity());
519061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        state.mItemCount = 1000;
520cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        glm.onAnchorReady(mRecyclerView.mRecycler, state, glm.mAnchorInfo);
52173304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should keep anchor in first span", 11, glm.mAnchorInfo.mPosition);
52273304eff156157f62075215e795e774803a6f96aYigit Boyar
52373304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 13;
524cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        glm.onAnchorReady(mRecyclerView.mRecycler, state, glm.mAnchorInfo);
52573304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 11, glm.mAnchorInfo.mPosition);
52673304eff156157f62075215e795e774803a6f96aYigit Boyar
52773304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 23;
528cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        glm.onAnchorReady(mRecyclerView.mRecycler, state, glm.mAnchorInfo);
52973304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 21, glm.mAnchorInfo.mPosition);
53073304eff156157f62075215e795e774803a6f96aYigit Boyar
53173304eff156157f62075215e795e774803a6f96aYigit Boyar        glm.mAnchorInfo.mPosition = 35;
532cf87e0b2827dba309646d6a73945b2e1d1fac248Yigit Boyar        glm.onAnchorReady(mRecyclerView.mRecycler, state, glm.mAnchorInfo);
53373304eff156157f62075215e795e774803a6f96aYigit Boyar        assertEquals("gm should move anchor to first span", 31, glm.mAnchorInfo.mPosition);
53473304eff156157f62075215e795e774803a6f96aYigit Boyar    }
53573304eff156157f62075215e795e774803a6f96aYigit Boyar
53673304eff156157f62075215e795e774803a6f96aYigit Boyar    public void testSpanLookup() {
537061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        spanLookupTest(false);
538061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
539061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
540061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testSpanLookupWithCache() {
541061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        spanLookupTest(true);
542061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
543061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
544061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testSpanLookupCache() {
545061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
546061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
547061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
548061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public int getSpanSize(int position) {
549061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (position > 6) {
550061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    return 2;
551061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
552061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                return 1;
553061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
554061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        };
555061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(true);
556061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child non existent", -1, ssl.findReferenceIndexFromCache(2));
557061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(4, 5);
558061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child non existent", -1, ssl.findReferenceIndexFromCache(3));
559061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        // this should not happen and if happens, it is better to return -1
560061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child itself", -1, ssl.findReferenceIndexFromCache(4));
561061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(5));
562061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(100));
563061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(6, 5);
564061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(7));
565061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 4, ssl.findReferenceIndexFromCache(6));
566061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child itself", -1, ssl.findReferenceIndexFromCache(4));
567061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.getCachedSpanIndex(12, 5);
568061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 12, ssl.findReferenceIndexFromCache(13));
569061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(12));
570061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before", 6, ssl.findReferenceIndexFromCache(7));
571061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 0; i < 6; i++) {
572061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            ssl.getCachedSpanIndex(i, 5);
573061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
574061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
575061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 1; i < 7; i++) {
576061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            assertEquals("reference child right before " + i, i - 1,
577061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ssl.findReferenceIndexFromCache(i));
578061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
579061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("reference child before 0 ", -1, ssl.findReferenceIndexFromCache(0));
580061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
581061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
582061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void spanLookupTest(boolean enableCache) {
58373304eff156157f62075215e795e774803a6f96aYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
58473304eff156157f62075215e795e774803a6f96aYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
58573304eff156157f62075215e795e774803a6f96aYigit Boyar            @Override
58673304eff156157f62075215e795e774803a6f96aYigit Boyar            public int getSpanSize(int position) {
58773304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 200) {
58873304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 100;
58973304eff156157f62075215e795e774803a6f96aYigit Boyar                }
59073304eff156157f62075215e795e774803a6f96aYigit Boyar                if (position > 6) {
59173304eff156157f62075215e795e774803a6f96aYigit Boyar                    return 2;
59273304eff156157f62075215e795e774803a6f96aYigit Boyar                }
59373304eff156157f62075215e795e774803a6f96aYigit Boyar                return 1;
59473304eff156157f62075215e795e774803a6f96aYigit Boyar            }
59573304eff156157f62075215e795e774803a6f96aYigit Boyar        };
596061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(enableCache);
597061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(0, 5));
598061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(4, ssl.getCachedSpanIndex(4, 5));
599061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(5, 5));
600061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(1, ssl.getCachedSpanIndex(6, 5));
601061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(2, ssl.getCachedSpanIndex(7, 5));
602061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(2, ssl.getCachedSpanIndex(9, 5));
603061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals(0, ssl.getCachedSpanIndex(8, 5));
604061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
605061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
6061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItem() throws Throwable {
6071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
6081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(VERTICAL).reverseLayout(false), 100, 0);
6091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
6101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
6111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverse() throws Throwable {
6121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
6131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(VERTICAL).reverseLayout(true), 100,
6141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                0);
6151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
6161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
6171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemHorizontal() throws Throwable {
6181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
6191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(HORIZONTAL).reverseLayout(
6201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
6211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
6221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
6231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void testRemoveAnchorItemReverseHorizontal() throws Throwable {
6241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
6251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config(3, 0).orientation(HORIZONTAL).reverseLayout(true),
6261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 0);
6271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
6281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
6291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    /**
6301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * This tests a regression where predictive animations were not working as expected when the
6311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * first item is removed and there aren't any more items to add from that direction.
6321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * First item refers to the default anchor item.
6331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     */
6341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void removeAnchorItemTest(final Config config, int adapterSize,
6351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final int removePos) throws Throwable {
6361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        GridTestAdapter adapter = new GridTestAdapter(adapterSize) {
6371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            @Override
6381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
6391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    int position) {
6401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                super.onBindViewHolder(holder, position);
6411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
6421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (!(lp instanceof ViewGroup.MarginLayoutParams)) {
6431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    lp = new ViewGroup.MarginLayoutParams(0, 0);
6441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    holder.itemView.setLayoutParams(lp);
6451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
6461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
6471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int maxSize;
6481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
6491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getWidth();
6501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = ViewGroup.MarginLayoutParams.FILL_PARENT;
6511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
6521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getHeight();
6531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = ViewGroup.MarginLayoutParams.FILL_PARENT;
6541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
6551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
6561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int desiredSize;
6571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (position == removePos) {
6581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it large
6591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 4;
6601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
6611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it small
6621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 8;
6631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
6641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
6651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = desiredSize;
6661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
6671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = desiredSize;
6681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
6691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
6701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        };
6711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        RecyclerView recyclerView = setupBasic(config, adapter);
6721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        waitForFirstLayout(recyclerView);
6731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int childCount = mGlm.getChildCount();
6741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        RecyclerView.ViewHolder toBeRemoved = null;
6751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>();
6761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < childCount; i++) {
6771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mGlm.getChildAt(i);
6781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
6791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (holder.getAdapterPosition() == removePos) {
6801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeRemoved = holder;
6811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            } else {
6821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeMoved.add(holder);
6831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
6841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
6851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertNotNull("test sanity", toBeRemoved);
6861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("test sanity", childCount - 1, toBeMoved.size());
6871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator();
6881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mRecyclerView.setItemAnimator(loggingItemAnimator);
6891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.reset();
6901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.expectRunPendingAnimationsCall(1);
6911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mGlm.expectLayout(2);
6921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        adapter.deleteAndNotify(removePos, 1);
6931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mGlm.waitForLayout(1);
6941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.waitForPendingAnimationsCall(2);
6951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("removed child should receive remove animation",
6961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mRemoveVHs.contains(toBeRemoved));
6971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder vh : toBeMoved) {
6981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("view holder should be in moved list",
6991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(vh));
7001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
7011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>();
7021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < mGlm.getChildCount(); i++) {
7031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mGlm.getChildAt(i);
7041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
7051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (toBeRemoved != holder && !toBeMoved.contains(holder)) {
7061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                newHolders.add(holder);
7071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
7081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
7091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("some new children should show up for the new space", newHolders.size() > 0);
7101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("no items should receive animate add since they are not new", 0,
7111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mAddVHs.size());
7121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder holder : newHolders) {
7131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("new holder should receive a move animation",
7141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(holder));
7151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
7161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        // for removed view, 3 for new row
7171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("control against adding too many children due to bad layout state preparation."
7181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(),
7191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                mRecyclerView.getChildCount() <= childCount + 1 + 3);
7201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
7211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
722a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    public void testSpanGroupIndex() {
723a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl
724a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                = new GridLayoutManager.SpanSizeLookup() {
725a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
726a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public int getSpanSize(int position) {
727a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (position > 200) {
728a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    return 100;
729a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
730a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                if (position > 6) {
731a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                    return 2;
732a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                }
733a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                return 1;
734a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
735a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        };
736a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(0, ssl.getSpanGroupIndex(0, 5));
737a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(0, ssl.getSpanGroupIndex(4, 5));
738a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(5, 5));
739a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(6, 5));
740a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(1, ssl.getSpanGroupIndex(7, 5));
741a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(2, ssl.getSpanGroupIndex(9, 5));
742a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals(2, ssl.getSpanGroupIndex(8, 5));
743a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
744a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar
745061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testNotifyDataSetChange() throws Throwable {
746061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView recyclerView = setupBasic(new Config(3, 100));
747061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final GridLayoutManager.SpanSizeLookup ssl = mGlm.getSpanSizeLookup();
748061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        ssl.setSpanIndexCacheEnabled(true);
749061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(recyclerView);
750061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertTrue("some positions should be cached", ssl.mSpanIndexCache.size() > 0);
751061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final Callback callback = new Callback() {
752061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
753061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void onBeforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
754061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (!state.isPreLayout()) {
755061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    assertEquals("cache should be empty", 0, ssl.mSpanIndexCache.size());
756061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
757061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
758061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
759061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
760061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void onAfterLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
761061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (!state.isPreLayout()) {
762061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    assertTrue("some items should be cached", ssl.mSpanIndexCache.size() > 0);
763061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
764061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
765061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        };
766061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.mCallbacks.add(callback);
767061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(2);
768061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.deleteAndNotify(2, 3);
769061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
770061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
771061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
772061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
773afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public void testUnevenHeights() throws Throwable {
774afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        final Map<Integer, RecyclerView.ViewHolder> viewHolderMap =
775afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new HashMap<Integer, RecyclerView.ViewHolder>();
776afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        RecyclerView recyclerView = setupBasic(new Config(3, 3), new GridTestAdapter(3) {
777afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            @Override
778afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
779afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    int position) {
780afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                super.onBindViewHolder(holder, position);
781afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                final GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
782afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                glp.height = 50 + position * 50;
783afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                viewHolderMap.put(position, holder);
784afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            }
785afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        });
786afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        waitForFirstLayout(recyclerView);
787afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
788afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should get max height", 150,
789afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getHeight());
790afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
791afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
792afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
793afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should have measured the max height", 150,
794afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getMeasuredHeight());
795afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
796afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
797afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
798afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    public void testUnevenWidths() throws Throwable {
799afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        final Map<Integer, RecyclerView.ViewHolder> viewHolderMap =
800afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new HashMap<Integer, RecyclerView.ViewHolder>();
801afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        RecyclerView recyclerView = setupBasic(new Config(3, HORIZONTAL, false),
802afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                new GridTestAdapter(3) {
803afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    @Override
804afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    public void onBindViewHolder(TestViewHolder holder,
805afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                            int position) {
806afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        super.onBindViewHolder(holder, position);
807afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        final GridLayoutManager.LayoutParams glp = ensureGridLp(holder.itemView);
808afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        glp.width = 50 + position * 50;
809afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                        viewHolderMap.put(position, holder);
810afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    }
811afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                });
812afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        waitForFirstLayout(recyclerView);
813afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
814afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should get max width", 150,
815afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getWidth());
816afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
817afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
818afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        for (RecyclerView.ViewHolder vh : viewHolderMap.values()) {
819afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar            assertEquals("all items should have measured the max width", 150,
820afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar                    vh.itemView.getMeasuredWidth());
821afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar        }
822afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar    }
823afa0494a97687b705feb3659385578f33f697ea9Yigit Boyar
824061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testScrollBackAndPreservePositions() throws Throwable {
825061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (Config config : mBaseVariations) {
826061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            config.mItemCount = 150;
827061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            scrollBackAndPreservePositionsTest(config);
828061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            removeRecyclerView();
829061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
830061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
831061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
832204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar    public void testSpanSizeChange() throws Throwable {
833204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100));
834204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        waitForFirstLayout(rv);
835204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        assertTrue(mGlm.supportsPredictiveItemAnimations());
836204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.expectLayout(1);
837204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        runTestOnUiThread(new Runnable() {
838204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar            @Override
839204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar            public void run() {
840204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar                mGlm.setSpanCount(5);
841204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar                assertFalse(mGlm.supportsPredictiveItemAnimations());
842204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar            }
843204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        });
844204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        checkForMainThreadException();
845204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.waitForLayout(2);
846204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.expectLayout(2);
847204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mAdapter.deleteAndNotify(3, 2);
848204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        mGlm.waitForLayout(2);
849204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar        assertTrue(mGlm.supportsPredictiveItemAnimations());
850204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar    }
851204f79ca6e0d6253c10a80da11056b03cb9d3fb0Yigit Boyar
852061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void testCacheSpanIndices() throws Throwable {
853061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(new Config(3, 100));
854061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.mSpanSizeLookup.setSpanIndexCacheEnabled(true);
855061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
856061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        GridLayoutManager.SpanSizeLookup ssl = mGlm.mSpanSizeLookup;
857061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertTrue("cache should be filled", mGlm.mSpanSizeLookup.mSpanIndexCache.size() > 0);
858061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("item index 5 should be in span 2", 2,
859061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                getLp(mGlm.findViewByPosition(5)).getSpanIndex());
860061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.expectLayout(2);
861061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.mFullSpanItems.add(4);
862061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mAdapter.changeAndNotify(4, 1);
863061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        mGlm.waitForLayout(2);
864061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        assertEquals("item index 5 should be in span 2", 0,
865061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                getLp(mGlm.findViewByPosition(5)).getSpanIndex());
866061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
867061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
868061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    GridLayoutManager.LayoutParams getLp(View view) {
869061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        return (GridLayoutManager.LayoutParams) view.getLayoutParams();
870061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
871061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
872061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    public void scrollBackAndPreservePositionsTest(final Config config) throws Throwable {
873061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final RecyclerView rv = setupBasic(config);
874061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        for (int i = 1; i < mAdapter.getItemCount(); i += config.mSpanCount + 2) {
875061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mAdapter.setFullSpan(i);
876061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
877061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        waitForFirstLayout(rv);
878061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int[] globalPositions = new int[mAdapter.getItemCount()];
879061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Arrays.fill(globalPositions, Integer.MIN_VALUE);
880061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int scrollStep = (mGlm.mOrientationHelper.getTotalSpace() / 20)
881061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                * (config.mReverseLayout ? -1 : 1);
882061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final String logPrefix = config.toString();
883061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        final int[] globalPos = new int[1];
884061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        runTestOnUiThread(new Runnable() {
885061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
886061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void run() {
8871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                assertSame("test sanity", mRecyclerView, rv);
888061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int globalScrollPosition = 0;
889061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int visited = 0;
890061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                while (visited < mAdapter.getItemCount()) {
891061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
892061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        View child = mRecyclerView.getChildAt(i);
893115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        final int pos = mRecyclerView.getChildLayoutPosition(child);
894061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (globalPositions[pos] != Integer.MIN_VALUE) {
895061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            continue;
896061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
897061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        visited++;
898061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
899061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                child.getLayoutParams();
900061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (config.mReverseLayout) {
901061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPositions[pos] = globalScrollPosition +
902061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
903061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
904061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPositions[pos] = globalScrollPosition +
905061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedStart(child);
906061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
907061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(logPrefix + " span index should match",
908061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
909061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                lp.getSpanIndex());
910061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
911061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    int scrolled = mGlm.scrollBy(scrollStep,
912061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
913061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    globalScrollPosition += scrolled;
914061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    if (scrolled == 0) {
915061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(
916061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                logPrefix + " If scroll is complete, all views should be visited",
917061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                visited, mAdapter.getItemCount());
918061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
919061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
920061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                if (DEBUG) {
921061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    Log.d(TAG, "done recording positions " + Arrays.toString(globalPositions));
922061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
923061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                globalPos[0] = globalScrollPosition;
924061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
925061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        });
926061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
9271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        // test sanity, ensure scroll happened
9281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        runTestOnUiThread(new Runnable() {
9291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            @Override
9301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            public void run() {
9311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int childCount = mGlm.getChildCount();
9321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final BitSet expectedPositions = new BitSet();
9331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                for (int i = 0; i < childCount; i ++) {
9341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    expectedPositions.set(mAdapter.getItemCount() - i - 1);
9351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
9361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                for (int i = 0; i <childCount; i ++) {
9371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    final View view = mGlm.getChildAt(i);
9381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    int position = mGlm.getPosition(view);
9391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    assertTrue("child position should be in last page", expectedPositions.get(position));
9401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
9411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
9421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        });
9431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        getInstrumentation().waitForIdleSync();
944061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        runTestOnUiThread(new Runnable() {
945061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            @Override
946061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            public void run() {
947061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int globalScrollPosition = globalPos[0];
948061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                // now scroll back and make sure global positions match
949061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                BitSet shouldTest = new BitSet(mAdapter.getItemCount());
950061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                shouldTest.set(0, mAdapter.getItemCount() - 1, true);
951061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                String assertPrefix = config
952061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        + " global pos must match when scrolling in reverse for position ";
953061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                int scrollAmount = Integer.MAX_VALUE;
954061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                while (!shouldTest.isEmpty() && scrollAmount != 0) {
955061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
956061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        View child = mRecyclerView.getChildAt(i);
957115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        int pos = mRecyclerView.getChildLayoutPosition(child);
958061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (!shouldTest.get(pos)) {
959061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            continue;
960061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
961061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
962061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                child.getLayoutParams();
963061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        shouldTest.clear(pos);
964061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        int globalPos;
965061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        if (config.mReverseLayout) {
966061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPos = globalScrollPosition +
967061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
968061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        } else {
969061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            globalPos = globalScrollPosition +
970061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                    mGlm.mOrientationHelper.getDecoratedStart(child);
971061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        }
972061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals(assertPrefix + pos,
973061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                globalPositions[pos], globalPos);
974061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                        assertEquals("span index should match",
975061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
976061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                                lp.getSpanIndex());
977061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    }
978061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    scrollAmount = mGlm.scrollBy(-scrollStep,
979061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
980061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    globalScrollPosition += scrollAmount;
981061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
982061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                assertTrue("all views should be seen", shouldTest.isEmpty());
983061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
984061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        });
985061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        checkForMainThreadException();
986061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
987061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
988061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class WrappedGridLayoutManager extends GridLayoutManager {
989061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
990061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        CountDownLatch mLayoutLatch;
991061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
992061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        List<Callback> mCallbacks = new ArrayList<Callback>();
993061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
994871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        Boolean mFakeRTL;
995871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
996061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public WrappedGridLayoutManager(Context context, int spanCount) {
997061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(context, spanCount);
998061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
999061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1000061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public WrappedGridLayoutManager(Context context, int spanCount, int orientation,
1001061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                boolean reverseLayout) {
1002061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(context, spanCount, orientation, reverseLayout);
1003061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1004061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1005061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        @Override
1006871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        protected boolean isLayoutRTL() {
1007871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            return mFakeRTL == null ? super.isLayoutRTL() : mFakeRTL;
1008871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
1009871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
1010871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        public void setFakeRtl(Boolean fakeRtl) {
1011871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            mFakeRTL = fakeRtl;
1012871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            try {
1013871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                requestLayoutOnUIThread(mRecyclerView);
1014871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            } catch (Throwable throwable) {
1015871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar                postExceptionToInstrumentation(throwable);
1016871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar            }
1017871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        }
1018871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar
1019871092be9f8b8b212a7bf16a9d1b735c3964ee9bYigit Boyar        @Override
1020061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1021061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            try {
1022061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (Callback callback : mCallbacks) {
1023061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    callback.onBeforeLayout(recycler, state);
1024061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
1025061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                super.onLayoutChildren(recycler, state);
1026061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                for (Callback callback : mCallbacks) {
1027061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    callback.onAfterLayout(recycler, state);
1028061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
1029061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            } catch (Throwable t) {
1030061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                postExceptionToInstrumentation(t);
1031061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
1032061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch.countDown();
1033061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1034061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1035888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        @Override
1036888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        LayoutState createLayoutState() {
1037888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            return new LayoutState() {
1038888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                @Override
1039888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                View next(RecyclerView.Recycler recycler) {
1040888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final boolean hadMore = hasMore(mRecyclerView.mState);
1041888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    final int position = mCurrentPosition;
1042888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    View next = super.next(recycler);
1043888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("if has more, should return a view", hadMore, next != null);
1044888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    assertEquals("position of the returned view must match current position",
1045888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
1046888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                    return next;
1047888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar                }
1048888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            };
1049888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        }
1050888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
1051061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void expectLayout(int layoutCount) {
1052061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch = new CountDownLatch(layoutCount);
1053061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1054061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1055061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void waitForLayout(int seconds) throws InterruptedException {
1056061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mLayoutLatch.await(seconds, SECONDS);
1057061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1058061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
1059061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1060061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class Config {
1061061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1062061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mSpanCount;
1063061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mOrientation = GridLayoutManager.VERTICAL;
1064061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        int mItemCount = 1000;
10657e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int mSpanPerItem = 1;
1066061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        boolean mReverseLayout = false;
1067061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1068061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Config(int spanCount, int itemCount) {
1069061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanCount = spanCount;
1070061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mItemCount = itemCount;
1071061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1072061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1073061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public Config(int spanCount, int orientation, boolean reverseLayout) {
1074061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mSpanCount = spanCount;
1075061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mOrientation = orientation;
1076061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mReverseLayout = reverseLayout;
1077061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1078061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1079061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Config orientation(int orientation) {
1080061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            mOrientation = orientation;
1081061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return this;
1082061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1083061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1084061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        @Override
1085061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public String toString() {
1086061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            return "Config{" +
1087061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    "mSpanCount=" + mSpanCount +
1088061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mOrientation=" + (mOrientation == GridLayoutManager.HORIZONTAL ? "h" : "v") +
1089061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mItemCount=" + mItemCount +
1090061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    ", mReverseLayout=" + mReverseLayout +
1091061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                    '}';
1092061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
10931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
10941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        public Config reverseLayout(boolean reverseLayout) {
10951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            mReverseLayout = reverseLayout;
10961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            return this;
10971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
10981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
10991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1100061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
1101061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1102061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class GridTestAdapter extends TestAdapter {
1103061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1104061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        Set<Integer> mFullSpanItems = new HashSet<Integer>();
11057e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        int mSpanPerItem = 1;
1106061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1107061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        GridTestAdapter(int count) {
1108061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            super(count);
1109061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1110061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
11117e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        GridTestAdapter(int count, int spanPerItem) {
11127e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            super(count);
11137e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko            mSpanPerItem = spanPerItem;
11147e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko        }
11157e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko
1116061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        void setFullSpan(int... items) {
1117061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            for (int i : items) {
1118061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                mFullSpanItems.add(i);
1119061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            }
1120061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1121061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1122061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        void assignSpanSizeLookup(final GridLayoutManager glm) {
1123061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
1124061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                @Override
1125061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                public int getSpanSize(int position) {
11267e6965e8f2c055634ce869bf78a4e4b32e4b0c1fAlexey Vitenko                    return mFullSpanItems.contains(position) ? glm.getSpanCount() : mSpanPerItem;
1127061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar                }
1128061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar            });
1129061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1130061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    }
1131061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1132061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar    class Callback {
1133061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1134061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onBeforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
1135061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
1136061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar
1137061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        public void onAfterLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
1138061c3284cf284424ae084799dd7ba5c8d6d59faaYigit Boyar        }
113973304eff156157f62075215e795e774803a6f96aYigit Boyar    }
114073304eff156157f62075215e795e774803a6f96aYigit Boyar}
1141