1edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project
3edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar *
4edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * you may not use this file except in compliance with the License.
6edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * You may obtain a copy of the License at
7edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar *
8edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar *
10edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * See the License for the specific language governing permissions and
14edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar * limitations under the License.
15edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar */
16edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget;
18edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
198052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport static org.junit.Assert.assertEquals;
208052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport static org.junit.Assert.assertFalse;
21270dbafc72ebe758c904300983a81336ede8b823Chris Craikimport static org.junit.Assert.assertNotEquals;
228052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport static org.junit.Assert.assertNotNull;
238052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport static org.junit.Assert.assertNotSame;
248052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport static org.junit.Assert.assertNull;
258052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport static org.junit.Assert.assertSame;
268052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport static org.junit.Assert.assertTrue;
27166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craikimport static org.junit.Assert.fail;
280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
29ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyarimport android.content.Context;
30da85cbdb44e810c9c9ade11f65fdf36a795061efAurimas Liutikasimport android.os.Build;
31c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyarimport android.os.Parcel;
32c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyarimport android.os.Parcelable;
33429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyarimport android.os.SystemClock;
340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.test.InstrumentationRegistry;
35754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.SmallTest;
360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.test.runner.AndroidJUnit4;
37ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyarimport android.util.AttributeSet;
38ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyarimport android.util.SparseArray;
39bf3e1bfa77a11365778fade25697acde1a70800aChris Craikimport android.view.LayoutInflater;
40429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyarimport android.view.MotionEvent;
41edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyarimport android.view.View;
42edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyarimport android.view.ViewGroup;
43231e669441b388f220c6a9a0ae46b5c2f1b4d938susnataimport android.view.animation.Interpolator;
44231e669441b388f220c6a9a0ae46b5c2f1b4d938susnataimport android.view.animation.LinearInterpolator;
451e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikimport android.widget.FrameLayout;
4650f048ddcd71fbb313f0673336920113cad28ee8Evan Roskyimport android.widget.LinearLayout;
47edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyarimport android.widget.TextView;
48edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
49c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.NonNull;
50c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.recyclerview.test.R;
51c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas
528052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport org.junit.Before;
538052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport org.junit.Test;
548052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikasimport org.junit.runner.RunWith;
558052423a9ffce385de32ae76fa962e6316f03677Aurimas Liutikas
568123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craikimport java.lang.ref.WeakReference;
5771c54eb5572a3e93409024640b8af5740620085cYigit Boyarimport java.util.ArrayList;
5871c54eb5572a3e93409024640b8af5740620085cYigit Boyarimport java.util.List;
59c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyarimport java.util.UUID;
60c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
61f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar@SmallTest
620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@RunWith(AndroidJUnit4.class)
630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpublic class RecyclerViewBasicTest {
64edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
65edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    RecyclerView mRecyclerView;
66edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Before
680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void setUp() throws Exception {
690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mRecyclerView = new RecyclerView(getContext());
700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    private Context getContext() {
73bf3e1bfa77a11365778fade25697acde1a70800aChris Craik        return InstrumentationRegistry.getTargetContext();
74edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
75edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void measureWithoutLayoutManager() {
78abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        measure();
79edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
80edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
81edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    private void measure() {
82edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.measure(View.MeasureSpec.AT_MOST | 320, View.MeasureSpec.AT_MOST | 240);
83edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
84edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
85edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    private void layout() {
86edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.layout(0, 0, 320, 320);
87edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
88edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
89abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    private void focusSearch() {
90abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.focusSearch(1);
91edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
92edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void layoutWithoutAdapter() throws InterruptedException {
95edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        MockLayoutManager layoutManager = new MockLayoutManager();
96edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setLayoutManager(layoutManager);
97abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        layout();
98edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        assertEquals("layout manager should not be called if there is no adapter attached",
99edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                0, layoutManager.mLayoutCount);
100edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
101edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
1026bc164c64d7f4cea6a1f155439269fd189a77d3bYigit Boyar    public void setScrollContainer() {
1036bc164c64d7f4cea6a1f155439269fd189a77d3bYigit Boyar        assertEquals("RecyclerView should announce itself as scroll container for the IME to "
1046bc164c64d7f4cea6a1f155439269fd189a77d3bYigit Boyar                + "handle it properly", true, mRecyclerView.isScrollContainer());
1056bc164c64d7f4cea6a1f155439269fd189a77d3bYigit Boyar    }
1066bc164c64d7f4cea6a1f155439269fd189a77d3bYigit Boyar
1070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void layoutWithoutLayoutManager() throws InterruptedException {
109abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
110abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        measure();
111abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        layout();
112abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
113abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusWithoutLayoutManager() throws InterruptedException {
116abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
117abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        measure();
118abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        layout();
119abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        focusSearch();
120abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
121abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
1220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollWithoutLayoutManager() throws InterruptedException {
124abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
125abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        measure();
126abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        layout();
127abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.scrollBy(10, 10);
128abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
129abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
1300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void smoothScrollWithoutLayoutManager() throws InterruptedException {
132abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
133abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        measure();
134abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        layout();
135abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.smoothScrollBy(10, 10);
136abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
137abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
1380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollToPositionWithoutLayoutManager() throws InterruptedException {
140abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
141abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        measure();
142abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        layout();
143abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.scrollToPosition(5);
144abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
145abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
1460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void smoothScrollToPositionWithoutLayoutManager() throws InterruptedException {
148abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
149abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        measure();
150abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        layout();
151abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        mRecyclerView.smoothScrollToPosition(5);
152abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
153abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
1540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void interceptTouchWithoutLayoutManager() {
156429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
157429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        measure();
158429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        layout();
159429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        assertFalse(mRecyclerView.onInterceptTouchEvent(
160429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar                MotionEvent.obtain(SystemClock.uptimeMillis(),
161429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar                        SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 10, 10, 0)));
162429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar    }
163429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar
1640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void onTouchWithoutLayoutManager() {
166429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        mRecyclerView.setAdapter(new MockAdapter(20));
167429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        measure();
168429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        layout();
169429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar        assertFalse(mRecyclerView.onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
170429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar                SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 10, 10, 0)));
171429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar    }
172429a9029d941a91972cd7601aa7e6d3718872c65Yigit Boyar
1730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void layoutSimple() throws InterruptedException {
175edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        MockLayoutManager layoutManager = new MockLayoutManager();
176edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setLayoutManager(layoutManager);
177edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setAdapter(new MockAdapter(3));
178edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        layout();
179edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        assertEquals("when both layout manager and activity is set, recycler view should call"
180edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                + " layout manager's layout method", 1, layoutManager.mLayoutCount);
181edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
182edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void observingAdapters() {
185edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        MockAdapter adapterOld = new MockAdapter(1);
186edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setAdapter(adapterOld);
187edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        assertTrue("attached adapter should have observables", adapterOld.hasObservers());
188edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
189edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        MockAdapter adapterNew = new MockAdapter(2);
190edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setAdapter(adapterNew);
191edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        assertFalse("detached adapter should lose observable", adapterOld.hasObservers());
192edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        assertTrue("new adapter should have observers", adapterNew.hasObservers());
193edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
194edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setAdapter(null);
195edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        assertNull("adapter should be removed successfully", mRecyclerView.getAdapter());
196edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        assertFalse("when adapter is removed, observables should be removed too",
197edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                adapterNew.hasObservers());
198edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
199edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterChangeCallbacks() {
202edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        MockLayoutManager layoutManager = new MockLayoutManager();
203edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setLayoutManager(layoutManager);
204edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        MockAdapter adapterOld = new MockAdapter(1);
205edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setAdapter(adapterOld);
206edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        layoutManager.assertPrevNextAdapters(null, adapterOld);
207edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
208edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        MockAdapter adapterNew = new MockAdapter(2);
209edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setAdapter(adapterNew);
210edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        layoutManager.assertPrevNextAdapters("switching adapters should trigger correct callbacks"
211edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                , adapterOld, adapterNew);
212edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
213edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        mRecyclerView.setAdapter(null);
214edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        layoutManager.assertPrevNextAdapters(
215edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                "Setting adapter null should trigger correct callbacks",
216edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                adapterNew, null);
217edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
218edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
2190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recyclerOffsetsOnMove() {
22171c54eb5572a3e93409024640b8af5740620085cYigit Boyar        MockLayoutManager  layoutManager = new MockLayoutManager();
22271c54eb5572a3e93409024640b8af5740620085cYigit Boyar        final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>();
22371c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.setLayoutManager(layoutManager);
22471c54eb5572a3e93409024640b8af5740620085cYigit Boyar        MockAdapter adapter = new MockAdapter(100) {
22571c54eb5572a3e93409024640b8af5740620085cYigit Boyar            @Override
2268a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
22771c54eb5572a3e93409024640b8af5740620085cYigit Boyar                super.onViewRecycled(holder);
22871c54eb5572a3e93409024640b8af5740620085cYigit Boyar                recycledVhs.add(holder);
22971c54eb5572a3e93409024640b8af5740620085cYigit Boyar            }
23071c54eb5572a3e93409024640b8af5740620085cYigit Boyar        };
23171c54eb5572a3e93409024640b8af5740620085cYigit Boyar        MockViewHolder mvh = new MockViewHolder(new TextView(getContext()));
23271c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.setAdapter(adapter);
23371c54eb5572a3e93409024640b8af5740620085cYigit Boyar        adapter.bindViewHolder(mvh, 20);
23471c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.mRecycler.mCachedViews.add(mvh);
23571c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.offsetPositionRecordsForRemove(10, 9, false);
23671c54eb5572a3e93409024640b8af5740620085cYigit Boyar
23771c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.offsetPositionRecordsForRemove(11, 1, false);
23871c54eb5572a3e93409024640b8af5740620085cYigit Boyar        assertEquals(1, recycledVhs.size());
23971c54eb5572a3e93409024640b8af5740620085cYigit Boyar        assertSame(mvh, recycledVhs.get(0));
24071c54eb5572a3e93409024640b8af5740620085cYigit Boyar    }
24171c54eb5572a3e93409024640b8af5740620085cYigit Boyar
2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recyclerOffsetsOnAdd() {
24471c54eb5572a3e93409024640b8af5740620085cYigit Boyar        MockLayoutManager  layoutManager = new MockLayoutManager();
24571c54eb5572a3e93409024640b8af5740620085cYigit Boyar        final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>();
24671c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.setLayoutManager(layoutManager);
24771c54eb5572a3e93409024640b8af5740620085cYigit Boyar        MockAdapter adapter = new MockAdapter(100) {
24871c54eb5572a3e93409024640b8af5740620085cYigit Boyar            @Override
2498a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
25071c54eb5572a3e93409024640b8af5740620085cYigit Boyar                super.onViewRecycled(holder);
25171c54eb5572a3e93409024640b8af5740620085cYigit Boyar                recycledVhs.add(holder);
25271c54eb5572a3e93409024640b8af5740620085cYigit Boyar            }
25371c54eb5572a3e93409024640b8af5740620085cYigit Boyar        };
25471c54eb5572a3e93409024640b8af5740620085cYigit Boyar        MockViewHolder mvh = new MockViewHolder(new TextView(getContext()));
25571c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.setAdapter(adapter);
25671c54eb5572a3e93409024640b8af5740620085cYigit Boyar        adapter.bindViewHolder(mvh, 20);
25771c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.mRecycler.mCachedViews.add(mvh);
25871c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.offsetPositionRecordsForRemove(10, 9, false);
25971c54eb5572a3e93409024640b8af5740620085cYigit Boyar
26071c54eb5572a3e93409024640b8af5740620085cYigit Boyar        mRecyclerView.offsetPositionRecordsForInsert(15, 10);
26171c54eb5572a3e93409024640b8af5740620085cYigit Boyar        assertEquals(11, mvh.mPosition);
26271c54eb5572a3e93409024640b8af5740620085cYigit Boyar    }
26371c54eb5572a3e93409024640b8af5740620085cYigit Boyar
2640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void savedStateWithStatelessLayoutManager() throws InterruptedException {
266c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        mRecyclerView.setLayoutManager(new MockLayoutManager() {
267c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            @Override
268c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            public Parcelable onSaveInstanceState() {
269c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                return null;
270c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            }
271c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        });
272c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(3));
273c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        Parcel parcel = Parcel.obtain();
274c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        String parcelSuffix = UUID.randomUUID().toString();
275c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        Parcelable savedState = mRecyclerView.onSaveInstanceState();
276c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        savedState.writeToParcel(parcel, 0);
277c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        parcel.writeString(parcelSuffix);
278c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
279c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        // reset position for reading
280c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        parcel.setDataPosition(0);
2810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        RecyclerView restored = new RecyclerView(getContext());
282c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        restored.setLayoutManager(new MockLayoutManager());
283c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(3));
284c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        // restore
285c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
286c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        restored.onRestoreInstanceState(savedState);
287c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
288c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
289c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                parcel.readString());
290c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        assertEquals("When unmarshalling, all of the parcel should be read", 0, parcel.dataAvail());
291c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
292c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar    }
293c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
2940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void savedState() throws InterruptedException {
296c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        MockLayoutManager mlm = new MockLayoutManager();
297c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        mRecyclerView.setLayoutManager(mlm);
298c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        mRecyclerView.setAdapter(new MockAdapter(3));
299c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        layout();
300c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        Parcelable savedState = mRecyclerView.onSaveInstanceState();
301c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        // we append a suffix to the parcelable to test out of bounds
302c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        String parcelSuffix = UUID.randomUUID().toString();
303c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        Parcel parcel = Parcel.obtain();
304c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        savedState.writeToParcel(parcel, 0);
305c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        parcel.writeString(parcelSuffix);
306c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
307c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        // reset for reading
308c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        parcel.setDataPosition(0);
309c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        // re-create
310c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
311c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
3120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        RecyclerView restored = new RecyclerView(getContext());
313c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        MockLayoutManager mlmRestored = new MockLayoutManager();
314c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        restored.setLayoutManager(mlmRestored);
315c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        restored.setAdapter(new MockAdapter(3));
316c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        restored.onRestoreInstanceState(savedState);
317c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
318c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
319c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                parcel.readString());
320c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        assertEquals("When unmarshalling, all of the parcel should be read", 0, parcel.dataAvail());
321c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        assertEquals("uuid in layout manager should be preserved properly", mlm.mUuid,
322c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                mlmRestored.mUuid);
323c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        assertNotSame("stateless parameter should not be preserved", mlm.mLayoutCount,
324c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                mlmRestored.mLayoutCount);
325c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        layout();
326c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar    }
327c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
3280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
3290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dontSaveChildrenState() throws InterruptedException {
330ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        MockLayoutManager mlm = new MockLayoutManager() {
331ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            @Override
332ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
333ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                super.onLayoutChildren(recycler, state);
334ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                View view = recycler.getViewForPosition(0);
335ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                addView(view);
336ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                measureChildWithMargins(view, 0, 0);
337ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
338ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            }
339ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        };
340ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        mRecyclerView.setLayoutManager(mlm);
341ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        mRecyclerView.setAdapter(new MockAdapter(3) {
3428a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            @NonNull
343ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            @Override
3448a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public RecyclerView.ViewHolder onCreateViewHolder(
3458a11e6829c522aa1efcc903afa4c01d337082eabChris Craik                    @NonNull ViewGroup parent, int viewType) {
346ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                final LoggingView itemView = new LoggingView(parent.getContext());
3470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                //noinspection ResourceType
348ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                itemView.setId(3);
349ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                return new MockViewHolder(itemView);
350ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            }
351ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        });
352ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        measure();
353ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        layout();
354ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        View view = mRecyclerView.getChildAt(0);
355ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        assertNotNull("test sanity", view);
356ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        LoggingView loggingView = (LoggingView) view;
357ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        SparseArray<Parcelable> container = new SparseArray<Parcelable>();
358ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        mRecyclerView.saveHierarchyState(container);
359ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        assertEquals("children's save state method should not be called", 0,
360ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar                loggingView.getOnSavedInstanceCnt());
361ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar    }
362ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar
363231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata    @Test
364231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata    public void smoothScrollWithCustomInterpolator() {
365231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata        mRecyclerView.setLayoutManager(new MockLayoutManager());
366231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata        mRecyclerView.setAdapter(new MockAdapter(20));
367231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata        Interpolator interpolator = new LinearInterpolator();
368231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata        mRecyclerView.smoothScrollBy(0, 100, interpolator);
369231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata        assertSame(interpolator, mRecyclerView.mViewFlinger.mInterpolator);
370231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata
371231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata        mRecyclerView.smoothScrollBy(0, -100);
372231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata        assertSame(RecyclerView.sQuinticInterpolator, mRecyclerView.mViewFlinger.mInterpolator);
373231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata    }
374231e669441b388f220c6a9a0ae46b5c2f1b4d938susnata
375ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik    @Test
376bf3e1bfa77a11365778fade25697acde1a70800aChris Craik    public void createAttachedException() {
377bf3e1bfa77a11365778fade25697acde1a70800aChris Craik        mRecyclerView.setAdapter(new RecyclerView.Adapter() {
378bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            @NonNull
379bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            @Override
380bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
381bf3e1bfa77a11365778fade25697acde1a70800aChris Craik                    int viewType) {
382bf3e1bfa77a11365778fade25697acde1a70800aChris Craik                View view = LayoutInflater.from(parent.getContext())
383bf3e1bfa77a11365778fade25697acde1a70800aChris Craik                        .inflate(R.layout.item_view, parent, true)
384bf3e1bfa77a11365778fade25697acde1a70800aChris Craik                        .findViewById(R.id.item_view); // find child, since parent is returned
385bf3e1bfa77a11365778fade25697acde1a70800aChris Craik                return new RecyclerView.ViewHolder(view) {};
386bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            }
387bf3e1bfa77a11365778fade25697acde1a70800aChris Craik
388bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            @Override
389bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
390bf3e1bfa77a11365778fade25697acde1a70800aChris Craik                fail("shouldn't get here, should throw during create");
391bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            }
392bf3e1bfa77a11365778fade25697acde1a70800aChris Craik
393bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            @Override
394bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            public int getItemCount() {
395bf3e1bfa77a11365778fade25697acde1a70800aChris Craik                return 1;
396bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            }
397bf3e1bfa77a11365778fade25697acde1a70800aChris Craik        });
398bf3e1bfa77a11365778fade25697acde1a70800aChris Craik        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
399bf3e1bfa77a11365778fade25697acde1a70800aChris Craik
400bf3e1bfa77a11365778fade25697acde1a70800aChris Craik        try {
401bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            measure();
402bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            //layout();
403bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            fail("IllegalStateException expected");
404bf3e1bfa77a11365778fade25697acde1a70800aChris Craik        } catch (IllegalStateException e) {
405bf3e1bfa77a11365778fade25697acde1a70800aChris Craik            // expected
406bf3e1bfa77a11365778fade25697acde1a70800aChris Craik        }
407bf3e1bfa77a11365778fade25697acde1a70800aChris Craik    }
408bf3e1bfa77a11365778fade25697acde1a70800aChris Craik
409bf3e1bfa77a11365778fade25697acde1a70800aChris Craik    @Test
410ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik    public void prefetchChangesCacheSize() {
411945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        mRecyclerView.setAdapter(new MockAdapter(20));
412ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        MockLayoutManager mlm = new MockLayoutManager() {
413ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik            @Override
4141e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik            public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
4153104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik                    RecyclerView.LayoutManager.LayoutPrefetchRegistry prefetchManager) {
416945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                prefetchManager.addPosition(0, 0);
417945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                prefetchManager.addPosition(1, 0);
418945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                prefetchManager.addPosition(2, 0);
419ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik            }
420ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        };
421945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
422ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        RecyclerView.Recycler recycler = mRecyclerView.mRecycler;
423ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax);
424ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik        mRecyclerView.setLayoutManager(mlm);
425945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax);
426945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
427945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
4281797d0dedfc9c06a83efb9090f19e3ffd14b2677Chris Craik            // layout, so prefetches can occur
4291797d0dedfc9c06a83efb9090f19e3ffd14b2677Chris Craik            mRecyclerView.measure(View.MeasureSpec.EXACTLY | 100, View.MeasureSpec.EXACTLY | 100);
4301797d0dedfc9c06a83efb9090f19e3ffd14b2677Chris Craik            mRecyclerView.layout(0, 0, 100, 100);
4311797d0dedfc9c06a83efb9090f19e3ffd14b2677Chris Craik
4321797d0dedfc9c06a83efb9090f19e3ffd14b2677Chris Craik            // prefetch gets 3 items, so expands cache by 3
4331e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik            mRecyclerView.mPrefetchRegistry.collectPrefetchPositionsFromView(mRecyclerView, false);
4341797d0dedfc9c06a83efb9090f19e3ffd14b2677Chris Craik            assertEquals(3, mRecyclerView.mPrefetchRegistry.mCount);
435945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax);
436945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
437945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            // Reset to default by removing layout
438945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mRecyclerView.setLayoutManager(null);
439945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax);
440945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
441945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            // And restore by restoring layout
442945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mRecyclerView.setLayoutManager(mlm);
443945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax);
444945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        }
445ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik    }
446ec4b5429886bfe93707a85823cf40de107bd9dc6Chris Craik
447270dbafc72ebe758c904300983a81336ede8b823Chris Craik    @Test
448270dbafc72ebe758c904300983a81336ede8b823Chris Craik    public void getNanoTime() {
449270dbafc72ebe758c904300983a81336ede8b823Chris Craik        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
450270dbafc72ebe758c904300983a81336ede8b823Chris Craik            // check that it looks vaguely time-ish
451270dbafc72ebe758c904300983a81336ede8b823Chris Craik            long time = mRecyclerView.getNanoTime();
452270dbafc72ebe758c904300983a81336ede8b823Chris Craik            assertNotEquals(0, time);
453270dbafc72ebe758c904300983a81336ede8b823Chris Craik            assertNotEquals(time, mRecyclerView.getNanoTime());
454270dbafc72ebe758c904300983a81336ede8b823Chris Craik        } else {
455270dbafc72ebe758c904300983a81336ede8b823Chris Craik            // expect to avoid cost of system.nanoTime on older platforms that don't do prefetch
456270dbafc72ebe758c904300983a81336ede8b823Chris Craik            assertEquals(0, mRecyclerView.getNanoTime());
457270dbafc72ebe758c904300983a81336ede8b823Chris Craik        }
458270dbafc72ebe758c904300983a81336ede8b823Chris Craik    }
459270dbafc72ebe758c904300983a81336ede8b823Chris Craik
4601e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik    @Test
4611e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik    public void findNestedRecyclerView() {
4621e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        RecyclerView recyclerView = new RecyclerView(getContext());
4631e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(recyclerView));
4641e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik
4651e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        ViewGroup parent = new FrameLayout(getContext());
4661e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        assertEquals(null, RecyclerView.findNestedRecyclerView(parent));
4671e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        parent.addView(recyclerView);
4681e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(parent));
4691e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik
4701e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        ViewGroup grandParent = new FrameLayout(getContext());
4711e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        assertEquals(null, RecyclerView.findNestedRecyclerView(grandParent));
4721e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        grandParent.addView(parent);
4731e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(grandParent));
4741e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik    }
4751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik
4761e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik    @Test
4771e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik    public void clearNestedRecyclerViewIfNotNested() {
4781e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        RecyclerView recyclerView = new RecyclerView(getContext());
4791e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        ViewGroup parent = new FrameLayout(getContext());
4801e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        parent.addView(recyclerView);
4811e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        ViewGroup grandParent = new FrameLayout(getContext());
4821e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        grandParent.addView(parent);
4831e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik
4841e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        // verify trivial noop case
4851e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        RecyclerView.ViewHolder holder = new RecyclerView.ViewHolder(recyclerView) {};
4868123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik        holder.mNestedRecyclerView = new WeakReference<>(recyclerView);
4871e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
4888123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik        assertEquals(recyclerView, holder.mNestedRecyclerView.get());
4891e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik
4901e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        // verify clear case
4911e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        holder = new RecyclerView.ViewHolder(new View(getContext())) {};
4928123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik        holder.mNestedRecyclerView = new WeakReference<>(recyclerView);
4931e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
4941e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        assertNull(holder.mNestedRecyclerView);
4951e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik
4961e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        // verify more deeply nested case
4971e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        holder = new RecyclerView.ViewHolder(grandParent) {};
4988123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik        holder.mNestedRecyclerView = new WeakReference<>(recyclerView);
4991e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik        RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
5008123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik        assertEquals(recyclerView, holder.mNestedRecyclerView.get());
5011e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik    }
5021e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik
503166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik    @Test
504166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik    public void exceptionContainsClasses() {
50531a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik        RecyclerView first = new RecyclerView(getContext());
50631a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik        first.setLayoutManager(new LinearLayoutManager(getContext()));
50731a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik        first.setAdapter(new MockAdapter(10));
508166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik
50931a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik        RecyclerView second = new RecyclerView(getContext());
510166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik        try {
51131a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik            second.setLayoutManager(first.getLayoutManager());
512166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik            fail("exception expected");
51331a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik        } catch (IllegalArgumentException e) {
51431a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik            // Note: exception contains first RV
51531a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik            String m = e.getMessage();
51631a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik            assertTrue("must contain RV class", m.contains(RecyclerView.class.getName()));
51731a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik            assertTrue("must contain Adapter class", m.contains(MockAdapter.class.getName()));
51831a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik            assertTrue("must contain LM class", m.contains(LinearLayoutManager.class.getName()));
51931a68d5eadcea9ebc14e797f1f65d628b206ec50Chris Craik            assertTrue("must contain ctx class", m.contains(getContext().getClass().getName()));
520166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik        }
521166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik    }
522166d5f1c4f2757966ff0ef167b6a2813e015b6a7Chris Craik
52350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky    @Test
52450f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky    public void focusOrderTest() {
52550f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        FocusOrderAdapter focusAdapter = new FocusOrderAdapter(getContext());
52650f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        mRecyclerView.setAdapter(focusAdapter);
52750f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
52850f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        measure();
52950f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        layout();
530396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard
531396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        boolean isIcsOrLower = Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1;
532396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard
533396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        // On API 15 and lower, focus forward get's translated to focus down.
534396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        View expected = isIcsOrLower ? focusAdapter.mBottomRight : focusAdapter.mBottomLeft;
535396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        assertEquals(expected, focusAdapter.mTopRight.focusSearch(View.FOCUS_FORWARD));
536396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard
537396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        // On API 15 and lower, focus forward get's translated to focus down, which in this case
538396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        // runs out of the RecyclerView, thus returning null.
539396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        expected = isIcsOrLower ? null : focusAdapter.mBottomRight;
540396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard        assertSame(expected, focusAdapter.mBottomLeft.focusSearch(View.FOCUS_FORWARD));
541396f5b41fbfdd23adf2a99d3f4c8ddb0c907c41ashepshapard
54250f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        // we don't want looping within RecyclerView
54350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        assertNull(focusAdapter.mBottomRight.focusSearch(View.FOCUS_FORWARD));
54450f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        assertNull(focusAdapter.mTopLeft.focusSearch(View.FOCUS_BACKWARD));
54550f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky    }
54650f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
547c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
548c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void setAdapter_callsCorrectLmMethods() throws Throwable {
549c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        MockLayoutManager mockLayoutManager = new MockLayoutManager();
550c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        MockAdapter mockAdapter = new MockAdapter(1);
551c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mRecyclerView.setLayoutManager(mockLayoutManager);
552c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
553c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mRecyclerView.setAdapter(mockAdapter);
554c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        layout();
555c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
556c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, mockLayoutManager.mAdapterChangedCount);
557c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(0, mockLayoutManager.mItemsChangedCount);
558c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
559c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
560c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
561c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void swapAdapter_callsCorrectLmMethods() throws Throwable {
562c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        MockLayoutManager mockLayoutManager = new MockLayoutManager();
563c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        MockAdapter mockAdapter = new MockAdapter(1);
564c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mRecyclerView.setLayoutManager(mockLayoutManager);
565c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
566c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mRecyclerView.swapAdapter(mockAdapter, true);
567c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        layout();
568c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
569c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, mockLayoutManager.mAdapterChangedCount);
570c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, mockLayoutManager.mItemsChangedCount);
571c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
572c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
573c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
574c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void notifyDataSetChanged_callsCorrectLmMethods() throws Throwable {
575c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        MockLayoutManager mockLayoutManager = new MockLayoutManager();
576c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        MockAdapter mockAdapter = new MockAdapter(1);
577c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mRecyclerView.setLayoutManager(mockLayoutManager);
578c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mRecyclerView.setAdapter(mockAdapter);
579c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mockLayoutManager.mAdapterChangedCount = 0;
580c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mockLayoutManager.mItemsChangedCount = 0;
581c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
582c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mockAdapter.notifyDataSetChanged();
583c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        layout();
584c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
585c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(0, mockLayoutManager.mAdapterChangedCount);
586c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, mockLayoutManager.mItemsChangedCount);
587c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
588c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
589c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar    static class MockLayoutManager extends RecyclerView.LayoutManager {
590edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
591edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        int mLayoutCount = 0;
592edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
593edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        int mAdapterChangedCount = 0;
594c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        int mItemsChangedCount = 0;
595edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
596edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        RecyclerView.Adapter mPrevAdapter;
597edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
598edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        RecyclerView.Adapter mNextAdapter;
599edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
600c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        String mUuid = UUID.randomUUID().toString();
601c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
602edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        @Override
603edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        public void onAdapterChanged(RecyclerView.Adapter oldAdapter,
604edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                RecyclerView.Adapter newAdapter) {
605edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            super.onAdapterChanged(oldAdapter, newAdapter);
606edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            mPrevAdapter = oldAdapter;
607edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            mNextAdapter = newAdapter;
608edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            mAdapterChangedCount++;
609edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
610edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
611edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        @Override
612c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        public void onItemsChanged(RecyclerView recyclerView) {
613c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard            mItemsChangedCount++;
614c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        }
615c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
616c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        @Override
617d4a794983364c7b1801638abc7f332abee12b6e6Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
618edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            mLayoutCount += 1;
619edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
620edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
621edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        @Override
622c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        public Parcelable onSaveInstanceState() {
623c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            LayoutManagerSavedState lss = new LayoutManagerSavedState();
624c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            lss.mUuid = mUuid;
625c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            return lss;
626c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        }
627c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
628c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        @Override
629c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        public void onRestoreInstanceState(Parcelable state) {
630c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            super.onRestoreInstanceState(state);
631c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            if (state instanceof LayoutManagerSavedState) {
632c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                mUuid = ((LayoutManagerSavedState) state).mUuid;
633c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            }
634c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        }
635c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
636c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        @Override
637edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
638edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
639edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                    ViewGroup.LayoutParams.WRAP_CONTENT);
640edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
641edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
642edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        public void assertPrevNextAdapters(String message, RecyclerView.Adapter prevAdapter,
643edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                RecyclerView.Adapter nextAdapter) {
644edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            assertSame(message, prevAdapter, mPrevAdapter);
645edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            assertSame(message, nextAdapter, mNextAdapter);
646edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
647edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
648edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        public void assertPrevNextAdapters(RecyclerView.Adapter prevAdapter,
649edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                RecyclerView.Adapter nextAdapter) {
650edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            assertPrevNextAdapters("Adapters from onAdapterChanged callback should match",
651edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar                    prevAdapter, nextAdapter);
652edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
653bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
654bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
655bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
656bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                RecyclerView.State state) {
657bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return dx;
658bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
659bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
660bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
661bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
662bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                RecyclerView.State state) {
663bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return dy;
664bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
665bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
666bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
667bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public boolean canScrollHorizontally() {
668bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return true;
669bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
670bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
671bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
672bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public boolean canScrollVertically() {
673bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return true;
674bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
675edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
676edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
677c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar    static class LayoutManagerSavedState implements Parcelable {
678c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
679c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        String mUuid;
680c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
681c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        public LayoutManagerSavedState(Parcel in) {
682c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            mUuid = in.readString();
683c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        }
684c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
685c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        public LayoutManagerSavedState() {
686c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
687c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        }
688c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
689c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        @Override
690c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        public int describeContents() {
691c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            return 0;
692c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        }
693c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
694c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        @Override
695c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        public void writeToParcel(Parcel dest, int flags) {
696c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            dest.writeString(mUuid);
697c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        }
698c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
699c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        public static final Parcelable.Creator<LayoutManagerSavedState> CREATOR
700c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                = new Parcelable.Creator<LayoutManagerSavedState>() {
701c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            @Override
702c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            public LayoutManagerSavedState createFromParcel(Parcel in) {
703c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                return new LayoutManagerSavedState(in);
704c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            }
705c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
706c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            @Override
707c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            public LayoutManagerSavedState[] newArray(int size) {
708c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar                return new LayoutManagerSavedState[size];
709c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar            }
710c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        };
711c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar    }
712c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar
713c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar    static class MockAdapter extends RecyclerView.Adapter {
714edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
715edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        private int mCount = 0;
716edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
717d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
718c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar        MockAdapter(int count) {
719edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            this.mCount = count;
720edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
721edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
7228a11e6829c522aa1efcc903afa4c01d337082eabChris Craik        @NonNull
723edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        @Override
7248a11e6829c522aa1efcc903afa4c01d337082eabChris Craik        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
725edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            return new MockViewHolder(new TextView(parent.getContext()));
726edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
727edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
728edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        @Override
7298a11e6829c522aa1efcc903afa4c01d337082eabChris Craik        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
730edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
731edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
732edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
733edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        @Override
734edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        public int getItemCount() {
735edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            return mCount;
736edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
737d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
738d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        void removeItems(int start, int count) {
739d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            mCount -= count;
740d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            notifyItemRangeRemoved(start, count);
741d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
742d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
743d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        void addItems(int start, int count) {
744d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            mCount += count;
745d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            notifyItemRangeInserted(start, count);
746d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
747edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
748edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar
749c30b4fceb76f45348c20f8b15096e1e13ff0c42fYigit Boyar    static class MockViewHolder extends RecyclerView.ViewHolder {
750115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        public Object mItem;
751edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        public MockViewHolder(View itemView) {
752edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar            super(itemView);
753edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar        }
754edc25dd683986a905790b0e9ef124165b6dd8004Yigit Boyar    }
755ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar
756ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar    static class LoggingView extends TextView {
757ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        private int mOnSavedInstanceCnt = 0;
758ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar
759ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        public LoggingView(Context context) {
760ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            super(context);
761ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        }
762ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar
763ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        public LoggingView(Context context, AttributeSet attrs) {
764ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            super(context, attrs);
765ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        }
766ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar
767ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        public LoggingView(Context context, AttributeSet attrs, int defStyleAttr) {
768ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            super(context, attrs, defStyleAttr);
769ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        }
770ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar
771ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        @Override
772ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        public Parcelable onSaveInstanceState() {
773ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            mOnSavedInstanceCnt ++;
774ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            return super.onSaveInstanceState();
775ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        }
776ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar
777ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        public int getOnSavedInstanceCnt() {
778ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar            return mOnSavedInstanceCnt;
779ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar        }
780ca570ddc976f20eae6dcc40bed9f6dee0e749353Yigit Boyar    }
78150f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
78250f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky    static class FocusOrderAdapter extends RecyclerView.Adapter {
78350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        TextView mTopLeft;
78450f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        TextView mTopRight;
78550f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        TextView mBottomLeft;
78650f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        TextView mBottomRight;
78750f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
78850f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        FocusOrderAdapter(Context context) {
78950f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            mTopLeft = new TextView(context);
79050f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            mTopRight = new TextView(context);
79150f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            mBottomLeft = new TextView(context);
79250f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            mBottomRight = new TextView(context);
79350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            for (TextView tv : new TextView[]{mTopLeft, mTopRight, mBottomLeft, mBottomRight}) {
79450f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky                tv.setFocusableInTouchMode(true);
79550f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky                tv.setLayoutParams(new LinearLayout.LayoutParams(100, 100));
79650f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            }
79750f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            // create a scenario where the "first" focusable is to the right of the last one
79850f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            mTopLeft.setFocusable(false);
79950f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            mTopRight.getLayoutParams().width = 101;
80050f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            mTopLeft.getLayoutParams().width = 101;
80150f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        }
80250f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
80350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        @Override
8048a11e6829c522aa1efcc903afa4c01d337082eabChris Craik        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
80550f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            LinearLayout holder = new LinearLayout(parent.getContext());
80650f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            holder.setOrientation(LinearLayout.HORIZONTAL);
80750f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            return new MockViewHolder(holder);
80850f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        }
80950f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
81050f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        @Override
8118a11e6829c522aa1efcc903afa4c01d337082eabChris Craik        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
81250f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            LinearLayout l = (LinearLayout) holder.itemView;
81350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            l.removeAllViews();
81450f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            if (position == 0) {
81550f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky                l.addView(mTopLeft);
81650f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky                l.addView(mTopRight);
81750f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            } else {
81850f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky                l.addView(mBottomLeft);
81950f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky                l.addView(mBottomRight);
82050f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            }
82150f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        }
82250f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
82350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        @Override
82450f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        public int getItemCount() {
82550f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky            return 2;
82650f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        }
82750f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
82850f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        void removeItems(int start, int count) {
82950f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        }
83050f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky
83150f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        void addItems(int start, int count) {
83250f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky        }
83350f048ddcd71fbb313f0673336920113cad28ee8Evan Rosky    }
834bf3e1bfa77a11365778fade25697acde1a70800aChris Craik}
835