RecyclerViewLayoutTest.java revision d8d42d52f8ae53107ffa849b86496650182e24b8
1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/*
2d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Copyright (C) 2014 The Android Open Source Project
3d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
4d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Licensed under the Apache License, Version 2.0 (the "License");
5d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * you may not use this file except in compliance with the License.
6d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * You may obtain a copy of the License at
7d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
8d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *      http://www.apache.org/licenses/LICENSE-2.0
9d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
10d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Unless required by applicable law or agreed to in writing, software
11d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * distributed under the License is distributed on an "AS IS" BASIS,
12d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * See the License for the specific language governing permissions and
14d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * limitations under the License.
15d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase */
16d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
17d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepackage android.support.v7.widget;
19d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
203d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyarimport org.hamcrest.CoreMatchers;
212e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyarimport org.hamcrest.MatcherAssert;
22be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyarimport org.junit.After;
23be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyarimport org.junit.Before;
24be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyarimport org.junit.Test;
25be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyarimport org.junit.runner.RunWith;
263d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyarimport org.mockito.Mockito;
27be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar
28bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyarimport android.content.Context;
29bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyarimport android.support.annotation.Nullable;
30be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyarimport android.support.test.InstrumentationRegistry;
316d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyarimport android.graphics.Color;
32ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyarimport android.graphics.PointF;
33f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyarimport android.graphics.Rect;
34bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport android.os.SystemClock;
354143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.support.test.InstrumentationRegistry;
364143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.support.test.runner.AndroidJUnit4;
37ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyarimport android.support.v4.view.ViewCompat;
380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.v7.util.TouchUtils;
39f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyarimport android.test.suitebuilder.annotation.MediumTest;
40bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyarimport android.util.AttributeSet;
41ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyarimport android.util.Log;
42670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyarimport android.view.Gravity;
43bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport android.view.MotionEvent;
445ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyarimport android.view.View;
45bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport android.view.ViewConfiguration;
460b1059b711009601ff08354e2df6bfc45266e80aYigit Boyarimport android.view.ViewGroup;
474965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyarimport android.view.ViewTreeObserver;
48a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyarimport android.widget.LinearLayout;
490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyarimport android.widget.TextView;
505ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
51e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyarimport java.util.ArrayList;
52e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyarimport java.util.HashMap;
530b1059b711009601ff08354e2df6bfc45266e80aYigit Boyarimport java.util.List;
54e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyarimport java.util.Map;
55ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyarimport java.util.concurrent.CountDownLatch;
56d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit;
57d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.atomic.AtomicBoolean;
58d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.atomic.AtomicInteger;
59824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
60e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyarimport static android.support.v7.widget.RecyclerView.NO_POSITION;
61bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport static android.support.v7.widget.RecyclerView.SCROLL_STATE_DRAGGING;
624143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
63bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport static android.support.v7.widget.RecyclerView.SCROLL_STATE_SETTLING;
64e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyarimport static android.support.v7.widget.RecyclerView.getChildViewHolderInt;
65d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar
66d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyarimport static org.hamcrest.CoreMatchers.sameInstance;
670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.*;
683d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyarimport static org.mockito.Mockito.*;
694510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.is;
70be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar
71be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar@RunWith(AndroidJUnit4.class)
72f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar@MediumTest
73d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepublic class RecyclerViewLayoutTest extends BaseRecyclerViewInstrumentationTest {
74cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private static final int FLAG_HORIZONTAL = 1;
75cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private static final int FLAG_VERTICAL = 1 << 1;
76cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private static final int FLAG_FLING = 1 << 2;
77d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
787bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar    private static final boolean DEBUG = false;
7922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
80ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    private static final String TAG = "RecyclerViewLayoutTest";
81ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
8222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar    public RecyclerViewLayoutTest() {
8322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        super(DEBUG);
8422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar    }
85bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
86be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
87a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyWithoutChanges() throws Throwable {
88a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(false, false, false);
89a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
90a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
91a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
92a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyRequireLayout() throws Throwable {
93a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(true, false, false);
94a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
95a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
96a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
97a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyRemoveAdapter() throws Throwable {
98a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(false, true, false);
99a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
100a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
101a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
102a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyRemoveLayoutManager() throws Throwable {
103a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(false, false, true);
104a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
105a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
106a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    private void detachAttachGetReady(final boolean requestLayoutOnDetach,
107a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            final boolean removeAdapter, final boolean removeLayoutManager) throws Throwable {
108a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final LinearLayout ll1 = new LinearLayout(getActivity());
109a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final LinearLayout ll2 = new LinearLayout(getActivity());
110a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final LinearLayout ll3 = new LinearLayout(getActivity());
111a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
112a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
113a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        ll1.addView(ll2);
114a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        ll2.addView(ll3);
115a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        ll3.addView(rv);
116a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        TestLayoutManager layoutManager = new TestLayoutManager() {
117a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
118a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void onLayoutCompleted(RecyclerView.State state) {
119a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                super.onLayoutCompleted(state);
120a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                layoutLatch.countDown();
121a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
122a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
123a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
124a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
125a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                super.onDetachedFromWindow(view, recycler);
126a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                if (requestLayoutOnDetach) {
127a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    view.requestLayout();
128a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                }
129a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
130a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        };
131a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        rv.setLayoutManager(layoutManager);
132a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        rv.setAdapter(new TestAdapter(10));
133a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        layoutManager.expectLayouts(1);
134a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        runTestOnUiThread(new Runnable() {
135a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
136a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void run() {
137a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().addView(ll1);
138a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
139a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        });
140a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        layoutManager.waitForLayout(2);
141a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        runTestOnUiThread(new Runnable() {
142a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
143a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void run() {
144a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                ll1.removeView(ll2);
145a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
146a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        });
147a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        getInstrumentation().waitForIdleSync();
148a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        if (removeLayoutManager) {
149a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setLayoutManager(null);
150a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setLayoutManager(layoutManager);
151a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        }
152a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        if (removeAdapter) {
153a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setAdapter(null);
154a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setAdapter(new TestAdapter(10));
155a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        }
156a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final boolean requireLayout = requestLayoutOnDetach || removeAdapter || removeLayoutManager;
157a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        layoutManager.expectLayouts(1);
158a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        runTestOnUiThread(new Runnable() {
159a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
160a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void run() {
161a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                ll1.addView(ll2);
162a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                if (requireLayout) {
163a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertTrue(rv.hasPendingAdapterUpdates());
164a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertFalse(rv.mFirstLayoutComplete);
165a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                } else {
166a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertFalse(rv.hasPendingAdapterUpdates());
167a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertTrue(rv.mFirstLayoutComplete);
168a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                }
169a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
170a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        });
171a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        if (requireLayout) {
172a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            layoutManager.waitForLayout(2);
173a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        } else {
174a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            layoutManager.assertNoLayout("nothing is invalid, layout should not happen", 2);
175a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        }
176a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
177a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
178a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
179d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    public void focusSearchWithOtherFocusables() throws Throwable {
180d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        final LinearLayout container = new LinearLayout(getActivity());
181d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.setOrientation(LinearLayout.VERTICAL);
182d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
183d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        mRecyclerView = rv;
184d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        rv.setAdapter(new TestAdapter(10) {
185d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
186d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
187d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                    int position) {
188d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                super.onBindViewHolder(holder, position);
189d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                holder.itemView.setFocusableInTouchMode(true);
190d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                holder.itemView.setLayoutParams(
191d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
192d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                        ViewGroup.LayoutParams.WRAP_CONTENT));
193d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
194d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        });
195d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
196d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
197d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
198d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                detachAndScrapAttachedViews(recycler);
199d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                layoutRange(recycler, 0, 1);
200d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                layoutLatch.countDown();
201d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
202d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar
203d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Nullable
204d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
205d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            public View onFocusSearchFailed(View focused, int direction,
206d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                    RecyclerView.Recycler recycler,
207d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                    RecyclerView.State state) {
208d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                assertEquals(View.FOCUS_FORWARD, direction);
209d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                assertEquals(1, getChildCount());
210d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                View child0 = getChildAt(0);
211d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                View view = recycler.getViewForPosition(1);
212d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                addView(view);
213d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                measureChild(view, 0, 0);
214d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                layoutDecorated(view, 0, child0.getBottom(), getDecoratedMeasuredWidth(view),
215d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                        child0.getBottom() + getDecoratedMeasuredHeight(view));
216d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                return view;
217d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
218d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        };
219d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        tlm.setAutoMeasureEnabled(true);
220d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        rv.setLayoutManager(tlm);
221d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        TextView viewAbove = new TextView(getActivity());
222d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewAbove.setText("view above");
223d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewAbove.setFocusableInTouchMode(true);
224d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.addView(viewAbove);
225d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.addView(rv);
226d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        TextView viewBelow = new TextView(getActivity());
227d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewBelow.setText("view below");
228d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewBelow.setFocusableInTouchMode(true);
229d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.addView(viewBelow);
230d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        tlm.expectLayouts(1);
231d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        runTestOnUiThread(new Runnable() {
232d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
233d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            public void run() {
234d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                getActivity().getContainer().addView(container);
235d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
236d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        });
237d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar
238d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        tlm.waitForLayout(2);
239d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        requestFocus(viewAbove, true);
240d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        assertTrue(viewAbove.hasFocus());
241d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        View newFocused = focusSearch(viewAbove, View.FOCUS_FORWARD);
242d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        assertThat(newFocused, sameInstance(rv.getChildAt(0)));
243d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        newFocused = focusSearch(rv.getChildAt(0), View.FOCUS_FORWARD);
244d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        assertThat(newFocused, sameInstance(rv.getChildAt(1)));
245d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    }
246d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar
247d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    @Test
2484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxNoTranslation() throws Throwable {
2494510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
2504510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
2514510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
2524510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
2534510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(10, 10, 30, 50)));
2544510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
2554510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
2564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
2574510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
2584510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
2594510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxTranslateX() throws Throwable {
2604510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
2614510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
2624510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
2634510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
2644510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                ViewCompat.setTranslationX(view, 10);
2654510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(20, 10, 40, 50)));
2664510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
2674510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
2684510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
2694510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
2704510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
2714510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxTranslateY() throws Throwable {
2724510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
2734510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
2744510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
2754510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
2764510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                ViewCompat.setTranslationY(view, 10);
2774510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(10, 20, 30, 60)));
2784510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
2794510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
2804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
2814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
2824510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
2834510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxScaleX() throws Throwable {
2844510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
2854510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
2864510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
2874510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
2884510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                ViewCompat.setScaleX(view, 2);
2894510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(0, 10, 40, 50)));
2904510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
2914510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
2924510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
2934510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
2944510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
2954510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxScaleY() throws Throwable {
2964510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
2974510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
2984510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
2994510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
3004510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                ViewCompat.setScaleY(view, 2);
3014510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(10, -10, 30, 70)));
3024510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
3034510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
3044510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
3054510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
3064510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
3074510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxRotated() throws Throwable {
3084510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
3094510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
3104510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
3114510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
3124510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                ViewCompat.setRotation(view, 90);
3134510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(0, 20, 40, 40)));
3144510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
3154510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
3164510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
3174510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
3184510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
3194510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxRotatedWithDecorOffsets() throws Throwable {
3204510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
3214510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final TestAdapter adapter = new TestAdapter(1);
3224510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setAdapter(adapter);
3234510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
3244510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
3254510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
3264510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                    RecyclerView.State state) {
3274510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                outRect.set(1, 2, 3, 4);
3284510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
3294510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
3304510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        TestLayoutManager layoutManager = new TestLayoutManager() {
3314510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
3324510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3334510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                detachAndScrapAttachedViews(recycler);
3344510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                View view = recycler.getViewForPosition(0);
3354510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                addView(view);
3364510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.measure(
3374510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(20, View.MeasureSpec.EXACTLY),
3384510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(40, View.MeasureSpec.EXACTLY)
3394510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                );
3404510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                // trigger decor offsets calculation
3414510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                calculateItemDecorationsForChild(view, new Rect());
3424510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
3434510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                ViewCompat.setRotation(view, 90);
3444510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(RecyclerViewLayoutTest.this.getTransformedBoundingBox(view),
3454510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        is(new Rect(-4, 19, 42, 43)));
3464510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
3474510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                layoutLatch.countDown();
3484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
3494510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        };
3504510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setLayoutManager(layoutManager);
3514510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.expectLayouts(1);
3524510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        setRecyclerView(recyclerView);
3534510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.waitForLayout(2);
3544510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        checkForMainThreadException();
3554510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
3564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
3574510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    private Rect getTransformedBoundingBox(View child) {
3584510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        Rect rect = new Rect();
3594510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        mRecyclerView.getLayoutManager().getTransformedBoundingBox(child, true, rect);
3604510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        return rect;
3614510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
3624510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
3634510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void transformedBoundingBoxTest(final ViewRunnable layout) throws Throwable {
3644510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
3654510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final TestAdapter adapter = new TestAdapter(1);
3664510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setAdapter(adapter);
3674510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        TestLayoutManager layoutManager = new TestLayoutManager() {
3684510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
3694510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3704510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                detachAndScrapAttachedViews(recycler);
3714510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                View view = recycler.getViewForPosition(0);
3724510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                addView(view);
3734510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.measure(
3744510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(20, View.MeasureSpec.EXACTLY),
3754510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(40, View.MeasureSpec.EXACTLY)
3764510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                );
3774510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                layout.run(view);
3784510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                layoutLatch.countDown();
3794510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
3804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        };
3814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setLayoutManager(layoutManager);
3824510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.expectLayouts(1);
3834510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        setRecyclerView(recyclerView);
3844510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.waitForLayout(2);
3854510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        checkForMainThreadException();
3864510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
3874510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
3884510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
3890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void flingFrozen() throws Throwable {
390ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        testScrollFrozen(true);
391ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
392ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
393ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
3940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dragFrozen() throws Throwable {
395ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        testScrollFrozen(false);
396ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
397ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
3983d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    @Test
3993d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    public void requestRectOnScreenWithScrollOffset() throws Throwable {
4003d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
4013d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final LayoutAllLayoutManager tlm = spy(new LayoutAllLayoutManager());
4023d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final int scrollY = 50;
4033d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
4043d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
4053d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
4063d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                View view = new View(parent.getContext());
4073d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                view.setScrollY(scrollY);
4083d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                return new RecyclerView.ViewHolder(view) {
4093d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                };
4103d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            }
4113d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
4123d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
4133d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
4143d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            public int getItemCount() {
4153d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                return 1;
4163d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            }
4173d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        };
4183d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        recyclerView.setAdapter(adapter);
4193d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        recyclerView.setLayoutManager(tlm);
4203d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        tlm.expectLayouts(1);
4213d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        setRecyclerView(recyclerView);
4223d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        tlm.waitForLayout(1);
4233d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final View child = recyclerView.getChildAt(0);
4243d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        assertThat(child.getScrollY(), CoreMatchers.is(scrollY));
4253d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        runTestOnUiThread(new Runnable() {
4263d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
4273d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            public void run() {
4283d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                recyclerView.requestChildRectangleOnScreen(child, new Rect(3, 4, 5, 6), true);
4293d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                verify(tlm, times(1)).scrollVerticallyBy(eq(-46), any(RecyclerView.Recycler.class),
4303d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                        any(RecyclerView.State.class));
4313d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            }
4323d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        });
4333d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    }
4343d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar
4352ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata    @Test
4362ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata    public void reattachAndScrollCrash() throws Throwable {
4372ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        final RecyclerView recyclerView = new RecyclerView(getActivity());
4382ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        final TestLayoutManager tlm = new TestLayoutManager() {
4392ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
4402ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
4412ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public boolean canScrollVertically() {
4422ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                return true;
4432ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
4442ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
4452ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
4462ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
4472ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                layoutRange(recycler, 0, Math.min(state.getItemCount(), 10));
4482ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
4492ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
4502ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
4512ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
4522ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                                          RecyclerView.State state) {
4532ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                // Access views in the state (that might have been deleted).
4542ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                for (int  i = 10; i < state.getItemCount(); i++) {
4552ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                    recycler.getViewForPosition(i);
4562ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                }
4572ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                return dy;
4582ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
4592ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        };
4602ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
4612ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        final TestAdapter adapter = new TestAdapter(12);
4622ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
4632ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        recyclerView.setAdapter(adapter);
4642ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        recyclerView.setLayoutManager(tlm);
4652ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
4662ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        setRecyclerView(recyclerView);
4672ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
4682ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        runTestOnUiThread(new Runnable() {
4692ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
4702ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public void run() {
471a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().removeView(recyclerView);
472a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().addView(recyclerView);
4732ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                try {
4742ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                    adapter.deleteAndNotify(1, adapter.getItemCount() - 1);
4752ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                } catch (Throwable throwable) {
4762ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                    postExceptionToInstrumentation(throwable);
4772ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                }
4782ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                recyclerView.scrollBy(0, 10);
4792ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
4802ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        });
4812ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata    }
4822ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
483ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    private void testScrollFrozen(boolean fling) throws Throwable {
484ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        RecyclerView recyclerView = new RecyclerView(getActivity());
485ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
486ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int horizontalScrollCount = 3;
487ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int verticalScrollCount = 3;
488ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int horizontalVelocity = 1000;
489ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int verticalVelocity = 1000;
490ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger horizontalCounter = new AtomicInteger(horizontalScrollCount);
491ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger verticalCounter = new AtomicInteger(verticalScrollCount);
492ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestLayoutManager tlm = new TestLayoutManager() {
493ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
494ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollHorizontally() {
495ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
496ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
497ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
498ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
499ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollVertically() {
500ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
501ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
502ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
503ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
504ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
505ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutRange(recycler, 0, 10);
506ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutLatch.countDown();
507ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
508ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
509ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
510ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
511ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    RecyclerView.State state) {
512ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                if (verticalCounter.get() > 0) {
513ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    verticalCounter.decrementAndGet();
514ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    return dy;
515ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                }
516ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return 0;
517ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
518ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
519ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
520ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
521ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    RecyclerView.State state) {
522ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                if (horizontalCounter.get() > 0) {
523ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    horizontalCounter.decrementAndGet();
524ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    return dx;
525ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                }
526ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return 0;
527ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
528ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        };
529ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter = new TestAdapter(100);
530ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setAdapter(adapter);
531ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setLayoutManager(tlm);
532ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.expectLayouts(1);
533ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setRecyclerView(recyclerView);
534ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.waitForLayout(2);
535ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
536ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
537ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
538ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        if (fling) {
539ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            assertFalse("fling should be blocked", fling(horizontalVelocity, verticalVelocity));
540ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        } else { // drag
5410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TouchUtils.dragViewTo(getInstrumentation(), recyclerView,
542ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    Gravity.LEFT | Gravity.TOP,
543ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    mRecyclerView.getWidth() / 2, mRecyclerView.getHeight() / 2);
544ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        }
545ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's horizontal scroll cb must not run", horizontalScrollCount,
546ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                horizontalCounter.get());
547ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's vertical scroll cb must not run", verticalScrollCount,
548ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                verticalCounter.get());
549ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
550ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
551ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
552ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        if (fling) {
553ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            assertTrue("fling should be started", fling(horizontalVelocity, verticalVelocity));
554ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        } else { // drag
5550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TouchUtils.dragViewTo(getInstrumentation(), recyclerView,
556ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    Gravity.LEFT | Gravity.TOP,
557ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    mRecyclerView.getWidth() / 2, mRecyclerView.getHeight() / 2);
558ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        }
559ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's horizontal scroll cb must finishes", 0, horizontalCounter.get());
560ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's vertical scroll cb must finishes", 0, verticalCounter.get());
561ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
562ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
563ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
5647bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar    public void  testFocusSearchFailFrozen() throws Throwable {
565ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        RecyclerView recyclerView = new RecyclerView(getActivity());
5667bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar        final CountDownLatch focusLatch = new CountDownLatch(1);
567ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger focusSearchCalled = new AtomicInteger(0);
568ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestLayoutManager tlm = new TestLayoutManager() {
569ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
570ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollHorizontally() {
571ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
572ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
573ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
574ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
575ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollVertically() {
576ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
577ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
578ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
579ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
580ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
581ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutRange(recycler, 0, 10);
582ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutLatch.countDown();
583ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
584ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
585ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
586121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar            public View onFocusSearchFailed(View focused, int direction,
587121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    RecyclerView.Recycler recycler, RecyclerView.State state) {
588ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                focusSearchCalled.addAndGet(1);
5897bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar                focusLatch.countDown();
590ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return null;
591ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
592ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        };
593ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter = new TestAdapter(100);
594ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setAdapter(adapter);
595ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setLayoutManager(tlm);
596ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.expectLayouts(1);
597ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setRecyclerView(recyclerView);
598ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.waitForLayout(2);
599ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final View c = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
600959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", requestFocus(c, true));
601959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", c.hasFocus());
602ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
6034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        focusSearch(recyclerView, c, View.FOCUS_DOWN);
604ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("onFocusSearchFailed should not be called when layout is frozen",
605ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                0, focusSearchCalled.get());
606ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
607d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        focusSearch(c, View.FOCUS_DOWN);
6087bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar        assertTrue(focusLatch.await(2, TimeUnit.SECONDS));
609ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals(1, focusSearchCalled.get());
610ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
611ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
612d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    public View focusSearch(final ViewGroup parent, final View focused, final int direction)
6134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            throws Throwable {
614d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        final View[] result = new View[1];
6154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        runTestOnUiThread(new Runnable() {
6164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            @Override
6174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            public void run() {
618d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                result[0] = parent.focusSearch(focused, direction);
6194143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            }
6204143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        });
621d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        return result[0];
6224143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
6234143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
624ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
6250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void frozenAndChangeAdapter() throws Throwable {
626ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        RecyclerView recyclerView = new RecyclerView(getActivity());
627ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
628ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger focusSearchCalled = new AtomicInteger(0);
629ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestLayoutManager tlm = new TestLayoutManager() {
630ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
631ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollHorizontally() {
632ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
633ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
634ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
635ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
636ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollVertically() {
637ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
638ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
639ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
640ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
641ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
642ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutRange(recycler, 0, 10);
643ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutLatch.countDown();
644ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
645ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
646ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
647ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
648ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    RecyclerView.State state) {
649ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                focusSearchCalled.addAndGet(1);
650ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return null;
651ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
652ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        };
653ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter = new TestAdapter(100);
654ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setAdapter(adapter);
655ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setLayoutManager(tlm);
656ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.expectLayouts(1);
657ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setRecyclerView(recyclerView);
658ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.waitForLayout(2);
659ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
660ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
661ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter2 = new TestAdapter(1000);
662ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setAdapter(adapter2);
663ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertFalse(recyclerView.isLayoutFrozen());
664ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertSame(adapter2, recyclerView.getAdapter());
665ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
666ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
667ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter3 = new TestAdapter(1000);
668ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        swapAdapter(adapter3, true);
669ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertFalse(recyclerView.isLayoutFrozen());
670ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertSame(adapter3, recyclerView.getAdapter());
671ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
672ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
673ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
6748fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreChanged() throws Throwable {
6758fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
6768fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
6778fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
6788fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeChanged(3, 0);
6798fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
6808fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
6818fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
6828fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
6838fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
6848fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreChangedWithPayload() throws Throwable {
6858fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
6868fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
6878fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
6888fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeChanged(0, 0, new Object());
6898fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
6908fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
6918fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
6928fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
6938fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
6948fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreAdded() throws Throwable {
6958fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
6968fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
6978fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
6988fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeInserted(3, 0);
6998fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
7008fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
7018fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
7028fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
7038fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
7048fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreRemoved() throws Throwable {
7058fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
7068fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
7078fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
7088fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeRemoved(3, 0);
7098fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
7108fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
7118fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
7128fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
7138fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
7148fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIfItemMovedIntoItsOwnPlace() throws Throwable {
7158fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
7168fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
7178fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
7188fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemMoved(3, 3);
7198fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
7208fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
7218fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
7228fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
7238fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void unnecessaryNotifyEvents(final AdapterRunnable action) throws Throwable {
7248fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
7258fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        final TestAdapter adapter = new TestAdapter(5);
7268fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
7278fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
7288fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
7298fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                super.onLayoutChildren(recycler, state);
7308fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                layoutLatch.countDown();
7318fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
7328fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        };
7338fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        recyclerView.setLayoutManager(tlm);
7348fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        recyclerView.setAdapter(adapter);
7358fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.expectLayouts(1);
7368fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        setRecyclerView(recyclerView);
7378fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.waitForLayout(1);
7388fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        // ready
7398fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.expectLayouts(1);
7408fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        runTestOnUiThread(new Runnable() {
7418fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
7428fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run() {
7438fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                try {
7448fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                    action.run(adapter);
7458fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                } catch (Throwable throwable) {
7468fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                    postExceptionToInstrumentation(throwable);
7478fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                }
7488fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
7498fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
7508fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.assertNoLayout("dummy event should not trigger a layout", 1);
7518fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        checkForMainThreadException();
7528fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
7538fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
7548fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
7550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollToPositionCallback() throws Throwable {
7564965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
7574965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
7584965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            int scrollPos = RecyclerView.NO_POSITION;
7591a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
7604965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            @Override
7614965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
7624965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                layoutLatch.countDown();
7634965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                if (scrollPos == RecyclerView.NO_POSITION) {
7644965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                    layoutRange(recycler, 0, 10);
7654965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                } else {
7664965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                    layoutRange(recycler, scrollPos, scrollPos + 10);
7674965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                }
7684965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            }
7691a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
7704965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            @Override
7714965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            public void scrollToPosition(int position) {
7724965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                scrollPos = position;
7734965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                requestLayout();
7744965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            }
7754965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        };
7764965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        recyclerView.setLayoutManager(tlm);
7774965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        TestAdapter adapter = new TestAdapter(100);
7784965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        recyclerView.setAdapter(adapter);
7794965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        final AtomicInteger rvCounter = new AtomicInteger(0);
7804965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        final AtomicInteger viewGroupCounter = new AtomicInteger(0);
7814965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        recyclerView.getViewTreeObserver().addOnScrollChangedListener(
7824965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                new ViewTreeObserver.OnScrollChangedListener() {
7834965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                    @Override
7844965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                    public void onScrollChanged() {
7854965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                        viewGroupCounter.incrementAndGet();
7864965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                    }
7874965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                });
7884965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
7894965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            @Override
7904965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
7914965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                rvCounter.incrementAndGet();
7924965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                super.onScrolled(recyclerView, dx, dy);
7934965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            }
7944965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        });
7954965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.expectLayouts(1);
7963f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar
7974965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        setRecyclerView(recyclerView);
7984965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.waitForLayout(2);
7993f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        // wait for draw :/
8003f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        Thread.sleep(1000);
8013f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar
8023f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("RV on scroll should be called for initialization", 1, rvCounter.get());
8033f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("VTO on scroll should be called for initialization", 1,
8044965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                viewGroupCounter.get());
8054965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.expectLayouts(1);
806ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
807ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        scrollToPosition(3);
808ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.assertNoLayout("scrollToPosition should be ignored", 2);
809ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
8104965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        scrollToPosition(3);
8114965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.waitForLayout(2);
8123f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("RV on scroll should be called", 2, rvCounter.get());
8133f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("VTO on scroll should be called", 2, viewGroupCounter.get());
8144965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.expectLayouts(1);
8154965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        requestLayoutOnUIThread(recyclerView);
8164965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.waitForLayout(2);
8173f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        // wait for draw :/
8183f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        Thread.sleep(1000);
8194965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        assertEquals("on scroll should NOT be called", 2, rvCounter.get());
8204965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        assertEquals("on scroll should NOT be called", 2, viewGroupCounter.get());
821e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
822e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
823e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
824e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    public void scrollCalllbackOnVisibleRangeExpand() throws Throwable {
825e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 5}, new int[]{3, 6});
826e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
827e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
828e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
829e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    public void scrollCalllbackOnVisibleRangeShrink() throws Throwable {
830e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 6}, new int[]{3, 5});
831e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
832e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
833e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
834e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    public void scrollCalllbackOnVisibleRangeExpand2() throws Throwable {
835e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 5}, new int[]{2, 5});
836e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
837e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
838e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
839e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    public void scrollCalllbackOnVisibleRangeShrink2() throws Throwable {
840e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 6}, new int[]{2, 6});
841e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
842e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
843e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    private void scrollCallbackOnVisibleRangeChange(int itemCount, final int[] beforeRange,
844e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            final int[] afterRange) throws Throwable {
845e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
846e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        final AtomicBoolean beforeState = new AtomicBoolean(true);
847e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
848e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            @Override
849e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
850e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar                detachAndScrapAttachedViews(recycler);
851e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar                int[] range = beforeState.get() ? beforeRange : afterRange;
852e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar                layoutRange(recycler, range[0], range[1]);
853e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar                layoutLatch.countDown();
854e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            }
855e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        };
856e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        recyclerView.setLayoutManager(tlm);
857e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        final TestAdapter adapter = new TestAdapter(itemCount);
858e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        recyclerView.setAdapter(adapter);
859e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.expectLayouts(1);
860e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        setRecyclerView(recyclerView);
861e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.waitForLayout(1);
862e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
863e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        RecyclerView.OnScrollListener mockListener = mock(RecyclerView.OnScrollListener.class);
864e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        recyclerView.addOnScrollListener(mockListener);
865e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        verify(mockListener, never()).onScrolled(any(RecyclerView.class), anyInt(), anyInt());
8664965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar
867e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.expectLayouts(1);
868e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        beforeState.set(false);
869e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        requestLayoutOnUIThread(recyclerView);
870e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.waitForLayout(2);
871e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        checkForMainThreadException();
872e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        verify(mockListener).onScrolled(recyclerView, 0, 0);
8734965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar    }
874670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
875be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
8762e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar    public void addItemOnScroll() throws Throwable {
8772e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
8782e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final AtomicInteger start = new AtomicInteger(0);
8792e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
8802e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            @Override
8812e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
8822e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                layoutRange(recycler, start.get(), start.get() + 10);
8832e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                layoutLatch.countDown();
8842e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            }
8852e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        };
8862e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        recyclerView.setLayoutManager(tlm);
8872e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final TestAdapter adapter = new TestAdapter(100);
8882e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        recyclerView.setAdapter(adapter);
8892e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.expectLayouts(1);
8902e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        setRecyclerView(recyclerView);
8912e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.waitForLayout(1);
8922e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final Throwable[] error = new Throwable[1];
8932e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final AtomicBoolean calledOnScroll = new AtomicBoolean(false);
8942e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
8952e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            @Override
8962e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
8972e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                super.onScrolled(recyclerView, dx, dy);
8982e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                calledOnScroll.set(true);
8992e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                try {
9002e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                    adapter.addAndNotify(5, 20);
9012e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                } catch (Throwable throwable) {
9022e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                    error[0] = throwable;
9032e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                }
9042e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            }
9052e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        });
9062e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        start.set(4);
9072e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        MatcherAssert.assertThat("test sanity", calledOnScroll.get(), CoreMatchers.is(false));
9082e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.expectLayouts(1);
9092e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        requestLayoutOnUIThread(recyclerView);
9102e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.waitForLayout(2);
9112e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        checkForMainThreadException();
9122e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        MatcherAssert.assertThat("test sanity", calledOnScroll.get(), CoreMatchers.is(true));
9132e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        MatcherAssert.assertThat(error[0], CoreMatchers.nullValue());
9142e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar    }
9152e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar
9162e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar    @Test
9170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionEqual() throws Throwable {
918670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 3, 1000, 1000);
919670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
920670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
921be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreVertical() throws Throwable {
923670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(2, 3, 1000, 1000);
924670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
925670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
926be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreHorizontal() throws Throwable {
928670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 2, 1000, 1000);
929670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
930670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
931be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollHorizontalOnly() throws Throwable {
933670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 0, 1000, 0);
934670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
935670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
936be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollVerticalOnly() throws Throwable {
938670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(0, 3, 0, 1000);
939670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
940670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
941be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionEqualReverse() throws Throwable {
943670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 3, -1000, -1000);
944670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
945670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
946be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreVerticalReverse() throws Throwable {
948670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(2, 3, -1000, -1000);
949670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
950670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
951be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreHorizontalReverse() throws Throwable {
953670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 2, -1000, -1000);
954670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
955670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
956be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollHorizontalOnlyReverse() throws Throwable {
958670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 0, -1000, 0);
959670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
960670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
961be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
9620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollVerticalOnlyReverse() throws Throwable {
963670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(0, 3, 0, -1000);
964670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
965670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
966670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    public void scrollInBothDirection(int horizontalScrollCount, int verticalScrollCount,
967670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            int horizontalVelocity, int verticalVelocity)
968670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            throws Throwable {
969670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
970670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicInteger horizontalCounter = new AtomicInteger(horizontalScrollCount);
971670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicInteger verticalCounter = new AtomicInteger(verticalScrollCount);
972670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
973670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
974670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollHorizontally() {
975670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return true;
976670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
977670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
978670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
979670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollVertically() {
980670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return true;
981670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
982670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
983670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
984670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
985670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutRange(recycler, 0, 10);
986670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutLatch.countDown();
987670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
988670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
989670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
990670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
991670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
992670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                if (verticalCounter.get() > 0) {
993670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    verticalCounter.decrementAndGet();
994670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    return dy;
995670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                }
996670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return 0;
997670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
998670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
999670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1000670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
1001670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
1002670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                if (horizontalCounter.get() > 0) {
1003670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    horizontalCounter.decrementAndGet();
1004670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    return dx;
1005670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                }
1006670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return 0;
1007670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1008670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        };
1009670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        TestAdapter adapter = new TestAdapter(100);
1010670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setAdapter(adapter);
1011670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setLayoutManager(tlm);
1012670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.expectLayouts(1);
1013670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        setRecyclerView(recyclerView);
1014670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.waitForLayout(2);
1015670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        assertTrue("test sanity, fling must run", fling(horizontalVelocity, verticalVelocity));
1016670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        assertEquals("rv's horizontal scroll cb must run " + horizontalScrollCount + " times'", 0,
1017670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                horizontalCounter.get());
1018670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        assertEquals("rv's vertical scroll cb must run " + verticalScrollCount + " times'", 0,
1019670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                verticalCounter.get());
1020670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1021670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1022be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
10230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dragHorizontal() throws Throwable {
1024cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_HORIZONTAL);
1025670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1026670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1027be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
10280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dragVertical() throws Throwable {
1029cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_VERTICAL);
1030670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1031670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1032be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
10330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void flingHorizontal() throws Throwable {
1034cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_HORIZONTAL | FLAG_FLING);
1035670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1036670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1037be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
10380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void flingVertical() throws Throwable {
1039cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_VERTICAL | FLAG_FLING);
1040670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1041670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1042f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    @Test
10430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void nestedDragVertical() throws Throwable {
1044a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        TestedFrameLayout tfl = getActivity().getContainer();
1045f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1046f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1047cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_VERTICAL, 0);
1048f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    }
1049f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes
1050f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    @Test
10510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void nestedDragHorizontal() throws Throwable {
1052a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        TestedFrameLayout tfl = getActivity().getContainer();
1053f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1054f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1055cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_HORIZONTAL, 0);
1056f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    }
1057670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1058d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes    @Test
10590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void nestedDragHorizontalCallsStopNestedScroll() throws Throwable {
1060a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        TestedFrameLayout tfl = getActivity().getContainer();
1061d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1062d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1063d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        scrollInOtherOrientationTest(FLAG_HORIZONTAL, 0);
1064d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        assertTrue("onStopNestedScroll called", tfl.stopNestedScrollCalled());
1065d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes    }
1066d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
1067d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes    @Test
10680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void nestedDragVerticalCallsStopNestedScroll() throws Throwable {
1069a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        TestedFrameLayout tfl = getActivity().getContainer();
1070d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        tfl.setNestedScrollMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1071d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        tfl.setNestedFlingMode(TestedFrameLayout.TEST_NESTED_SCROLL_MODE_CONSUME);
1072d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        scrollInOtherOrientationTest(FLAG_VERTICAL, 0);
1073d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        assertTrue("onStopNestedScroll called", tfl.stopNestedScrollCalled());
1074d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes    }
1075d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
1076cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private void scrollInOtherOrientationTest(int flags)
1077f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes            throws Throwable {
1078364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar        scrollInOtherOrientationTest(flags, flags);
1079f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    }
1080f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes
1081cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private void scrollInOtherOrientationTest(final int flags, int expectedFlags) throws Throwable {
1082670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1083670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicBoolean scrolledHorizontal = new AtomicBoolean(false);
1084670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicBoolean scrolledVertical = new AtomicBoolean(false);
1085f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes
1086f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        final TestLayoutManager tlm = new TestLayoutManager() {
1087670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1088670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollHorizontally() {
1089cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                return (flags & FLAG_HORIZONTAL) != 0;
1090670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1091670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1092670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1093670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollVertically() {
1094cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                return (flags & FLAG_VERTICAL) != 0;
1095670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1096670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1097670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1098670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1099670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutRange(recycler, 0, 10);
1100670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutLatch.countDown();
1101670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1102670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1103670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1104670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
1105670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
1106670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                scrolledVertical.set(true);
1107f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes                return super.scrollVerticallyBy(dy, recycler, state);
1108670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1109670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1110670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1111670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
1112670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
1113670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                scrolledHorizontal.set(true);
1114f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes                return super.scrollHorizontallyBy(dx, recycler, state);
1115670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1116670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        };
1117670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        TestAdapter adapter = new TestAdapter(100);
1118670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setAdapter(adapter);
1119670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setLayoutManager(tlm);
1120670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.expectLayouts(1);
1121670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        setRecyclerView(recyclerView);
1122670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.waitForLayout(2);
1123cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        if ( (flags & FLAG_FLING) != 0 ) {
1124cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar            int flingVelocity = (mRecyclerView.getMaxFlingVelocity() +
1125cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                    mRecyclerView.getMinFlingVelocity()) / 2;
1126cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar            assertEquals("fling started", (expectedFlags & FLAG_FLING) != 0,
1127cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                    fling(flingVelocity, flingVelocity));
1128f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        } else { // drag
11290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TouchUtils.dragViewTo(getInstrumentation(), recyclerView, Gravity.LEFT | Gravity.TOP,
1130cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                    mRecyclerView.getWidth() / 2, mRecyclerView.getHeight() / 2);
1131670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        }
1132f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        assertEquals("horizontally scrolled: " + tlm.mScrollHorizontallyAmount,
1133cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                (expectedFlags & FLAG_HORIZONTAL) != 0, scrolledHorizontal.get());
1134f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        assertEquals("vertically scrolled: " + tlm.mScrollVerticallyAmount,
1135cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                (expectedFlags & FLAG_VERTICAL) != 0, scrolledVertical.get());
1136670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1137670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1138670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    private boolean fling(final int velocityX, final int velocityY) throws Throwable {
1139670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicBoolean didStart = new AtomicBoolean(false);
1140670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        runTestOnUiThread(new Runnable() {
1141670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1142670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public void run() {
1143670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                boolean result = mRecyclerView.fling(velocityX, velocityY);
1144670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                didStart.set(result);
1145670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1146670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        });
1147670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        if (!didStart.get()) {
1148670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            return false;
1149670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        }
1150959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        waitForIdleScroll(mRecyclerView);
1151670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        return true;
1152670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1153670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1154959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    private void assertPendingUpdatesAndLayoutTest(final AdapterRunnable runnable) throws Throwable {
1155959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1156959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        TestLayoutManager layoutManager = new DumbLayoutManager();
1157959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
1158959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        setupBasic(recyclerView, layoutManager, testAdapter, false);
1159959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        layoutManager.expectLayouts(1);
1160da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        runTestOnUiThread(new Runnable() {
1161da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
1162da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            public void run() {
1163959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                try {
1164959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    runnable.run(testAdapter);
1165959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                } catch (Throwable throwable) {
1166959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    fail("runnable has thrown an exception");
1167959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                }
1168da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar                assertTrue(mRecyclerView.hasPendingAdapterUpdates());
1169da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
1170da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
1171959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        layoutManager.waitForLayout(1);
1172da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        assertFalse(mRecyclerView.hasPendingAdapterUpdates());
1173959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        checkForMainThreadException();
1174da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
1175da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
1176da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    private void setupBasic(RecyclerView recyclerView, TestLayoutManager tlm,
1177da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            TestAdapter adapter, boolean waitForFirstLayout) throws Throwable {
1178da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        recyclerView.setLayoutManager(tlm);
1179da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        recyclerView.setAdapter(adapter);
1180da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        if (waitForFirstLayout) {
1181da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            tlm.expectLayouts(1);
1182da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            setRecyclerView(recyclerView);
1183da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            tlm.waitForLayout(1);
1184da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        } else {
1185da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            setRecyclerView(recyclerView);
1186da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        }
1187da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
1188da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
1189be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
11900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void hasPendingUpdatesBeforeFirstLayout() throws Throwable {
1191da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1192da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestLayoutManager layoutManager = new DumbLayoutManager();
1193da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
1194da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        setupBasic(recyclerView, layoutManager, testAdapter, false);
1195da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        assertTrue(mRecyclerView.hasPendingAdapterUpdates());
1196da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
1197da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
1198be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
11990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void noPendingUpdatesAfterLayout() throws Throwable {
1200da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1201da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestLayoutManager layoutManager = new DumbLayoutManager();
1202da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
1203da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        setupBasic(recyclerView, layoutManager, testAdapter, true);
1204da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        assertFalse(mRecyclerView.hasPendingAdapterUpdates());
1205da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
1206da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
1207be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
1208959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsRemoved() throws Throwable {
1209959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
1210da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
1211959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
1212959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.deleteAndNotify(1, 1);
1213da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
1214da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
1215959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
1216959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
1217959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsInserted() throws Throwable {
1218959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
1219da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
1220959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
1221959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.addAndNotify(2, 1);
1222da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
1223da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
1224959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
1225959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
1226959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsMoved() throws Throwable {
1227959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
1228da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
1229959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
1230959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.moveItem(2, 3, true);
1231da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
1232da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
1233959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
1234959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
1235959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsChanged() throws Throwable {
1236959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
1237da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
1238959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
1239959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.changeAndNotify(2, 1);
1240da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
1241da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
1242959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
1243959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
1244959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterDataSetIsChanged() throws Throwable {
1245959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
1246da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
1247959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) {
1248959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                mRecyclerView.getAdapter().notifyDataSetChanged();
1249da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
1250da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
1251da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
1252da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
1253be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
12540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void transientStateRecycleViaAdapter() throws Throwable {
1255e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        transientStateRecycleTest(true, false);
1256e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
1257e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
1258be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
12590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void transientStateRecycleViaTransientStateCleanup() throws Throwable {
1260e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        transientStateRecycleTest(false, true);
1261e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
1262e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
1263be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
12640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void transientStateDontRecycle() throws Throwable {
1265e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        transientStateRecycleTest(false, false);
1266e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
1267e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
1268e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    public void transientStateRecycleTest(final boolean succeed, final boolean unsetTransientState)
1269e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            throws Throwable {
1270e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        final List<View> failedToRecycle = new ArrayList<View>();
1271e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        final List<View> recycled = new ArrayList<View>();
1272e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10) {
1273e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            @Override
1274e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            public boolean onFailedToRecycleView(
1275e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    TestViewHolder holder) {
1276e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                failedToRecycle.add(holder.itemView);
1277e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                if (unsetTransientState) {
1278e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    setHasTransientState(holder.itemView, false);
1279e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
1280e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                return succeed;
1281e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            }
1282e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
1283e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            @Override
1284e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            public void onViewRecycled(TestViewHolder holder) {
1285e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                recycled.add(holder.itemView);
1286e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                super.onViewRecycled(holder);
1287e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            }
1288e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        };
1289e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
1290e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            @Override
1291e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1292e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                if (getChildCount() == 0) {
1293e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    detachAndScrapAttachedViews(recycler);
1294e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    layoutRange(recycler, 0, 5);
1295e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                } else {
1296e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    removeAndRecycleAllViews(recycler);
1297e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
1298e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                if (layoutLatch != null) {
1299e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    layoutLatch.countDown();
1300e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
1301e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            }
1302e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        };
1303e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1304e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        recyclerView.setAdapter(testAdapter);
1305e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        recyclerView.setLayoutManager(tlm);
1306e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        recyclerView.setItemAnimator(null);
1307e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        setRecyclerView(recyclerView);
1308e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        getInstrumentation().waitForIdleSync();
1309e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        // make sure we have enough views after this position so that we'll receive the on recycled
1310e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        // callback
1311e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        View view = recyclerView.getChildAt(3);//this has to be greater than def cache size.
1312e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        setHasTransientState(view, true);
1313e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        tlm.expectLayouts(1);
1314e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        requestLayoutOnUIThread(recyclerView);
1315e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        tlm.waitForLayout(2);
1316e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
1317e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        assertTrue(failedToRecycle.contains(view));
1318e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        assertEquals(succeed || unsetTransientState, recycled.contains(view));
1319e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar    }
1320e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar
1321be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
13220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionInvalidation() throws Throwable {
1323e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
1324e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        final TestAdapter adapter = new TestAdapter(10);
1325e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        final TestLayoutManager tlm = new TestLayoutManager() {
1326e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            @Override
1327e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1328e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                layoutRange(recycler, 0, state.getItemCount());
1329e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                layoutLatch.countDown();
1330e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            }
1331e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        };
1332e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        recyclerView.setAdapter(adapter);
1333e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        recyclerView.setLayoutManager(tlm);
1334e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        tlm.expectLayouts(1);
1335e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        setRecyclerView(recyclerView);
1336e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        tlm.waitForLayout(1);
1337e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        runTestOnUiThread(new Runnable() {
1338e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            @Override
1339e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            public void run() {
13401a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                for (int i = 0; i < tlm.getChildCount(); i++) {
1341e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                    assertNotSame("adapter positions should not be undefined",
1342e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            recyclerView.getChildAdapterPosition(tlm.getChildAt(i)),
1343e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            RecyclerView.NO_POSITION);
1344e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                }
1345e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                adapter.notifyDataSetChanged();
13461a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                for (int i = 0; i < tlm.getChildCount(); i++) {
1347e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                    assertSame("adapter positions should be undefined",
1348e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            recyclerView.getChildAdapterPosition(tlm.getChildAt(i)),
1349e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            RecyclerView.NO_POSITION);
1350e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                }
1351e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            }
1352e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        });
1353e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar    }
1354e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar
1355be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
13560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsBasic() throws Throwable {
1357115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(null);
1358115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
1359115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1360be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
13610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsRemoveItems() throws Throwable {
1362115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
1363115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1364115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
1365115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.deleteAndNotify(3, 4);
1366115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1367115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
1368115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
1369115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1370be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
13710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsRemoveItemsBefore() throws Throwable {
1372115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
1373115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1374115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
1375115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.deleteAndNotify(0, 1);
1376115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1377115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
1378115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
1379115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1380be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
13810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsAddItemsBefore() throws Throwable {
1382115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
1383115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1384115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
1385115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.addAndNotify(0, 5);
1386115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1387115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
1388115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
1389115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1390be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
13910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsAddItemsInside() throws Throwable {
1392115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
1393115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1394115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
1395115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.addAndNotify(3, 2);
1396115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1397115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
1398115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
1399115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1400be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
14010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsMoveItems() throws Throwable {
1402115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
1403115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1404115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
1405115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.moveAndNotify(3, 5);
1406115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1407115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
1408115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
1409115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1410be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
14110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsNotifyDataSetChanged() throws Throwable {
1412115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
1413115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1414115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
1415115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.mItems.clear();
14161a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                for (int i = 0; i < 20; i++) {
1417115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    adapter.mItems.add(new Item(i, "added item"));
1418115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                }
1419115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.notifyDataSetChanged();
1420115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1421115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
1422115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
1423115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1424be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
14250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void avoidLeakingRecyclerViewIfViewIsNotRecycled() throws Throwable {
1426619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final AtomicBoolean failedToRecycle = new AtomicBoolean(false);
1427619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
1428619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
1429619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
1430619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1431619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                detachAndScrapAttachedViews(recycler);
1432619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutRange(recycler, 0, state.getItemCount());
1433619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutLatch.countDown();
1434619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
1435619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        };
1436619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestAdapter adapter = new TestAdapter(10) {
1437619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
1438619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public boolean onFailedToRecycleView(
1439619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    TestViewHolder holder) {
1440619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                failedToRecycle.set(true);
1441619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                return false;
1442619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
1443619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        };
1444619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setAdapter(adapter);
1445619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setLayoutManager(tlm);
1446619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
1447619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        setRecyclerView(rv);
1448619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(1);
1449619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final RecyclerView.ViewHolder vh = rv.getChildViewHolder(rv.getChildAt(0));
1450619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        runTestOnUiThread(new Runnable() {
1451619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
1452619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public void run() {
1453619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                ViewCompat.setHasTransientState(vh.itemView, true);
1454619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
1455619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        });
1456619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
1457619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        adapter.deleteAndNotify(0, 10);
1458619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(2);
1459619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final CountDownLatch animationsLatch = new CountDownLatch(1);
1460619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.getItemAnimator().isRunning(
1461619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
1462619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    @Override
1463619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    public void onAnimationsFinished() {
1464619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                        animationsLatch.countDown();
1465619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    }
1466619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                });
1467619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertTrue(animationsLatch.await(2, TimeUnit.SECONDS));
1468619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertTrue(failedToRecycle.get());
1469619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertNull(vh.mOwnerRecyclerView);
1470619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        checkForMainThreadException();
1471619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar    }
1472619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar
1473be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
14740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void avoidLeakingRecyclerViewViaViewHolder() throws Throwable {
1475619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
1476619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
1477619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
1478619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1479619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                detachAndScrapAttachedViews(recycler);
1480619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutRange(recycler, 0, state.getItemCount());
1481619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutLatch.countDown();
1482619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
1483619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        };
1484619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
1485619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setAdapter(adapter);
1486619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setLayoutManager(tlm);
1487619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
1488619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        setRecyclerView(rv);
1489619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(1);
1490619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final RecyclerView.ViewHolder vh = rv.getChildViewHolder(rv.getChildAt(0));
1491619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
1492619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        adapter.deleteAndNotify(0, 10);
1493619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(2);
1494619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final CountDownLatch animationsLatch = new CountDownLatch(1);
1495619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.getItemAnimator().isRunning(
1496619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
1497619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    @Override
1498619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    public void onAnimationsFinished() {
1499619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                        animationsLatch.countDown();
1500619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    }
1501619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                });
1502619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertTrue(animationsLatch.await(2, TimeUnit.SECONDS));
1503619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertNull(vh.mOwnerRecyclerView);
1504619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        checkForMainThreadException();
1505619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar    }
1506619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar
1507bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    @Test
1508bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    public void duplicateAdapterPositionTest() throws Throwable {
1509bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
1510bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final TestLayoutManager tlm = new TestLayoutManager() {
1511bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            @Override
1512bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1513bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                detachAndScrapAttachedViews(recycler);
1514bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                layoutRange(recycler, 0, state.getItemCount());
1515bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                if (!state.isPreLayout()) {
1516bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    while (!recycler.getScrapList().isEmpty()) {
1517bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        RecyclerView.ViewHolder viewHolder = recycler.getScrapList().get(0);
1518bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        addDisappearingView(viewHolder.itemView, 0);
1519bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    }
1520bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                }
1521bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                layoutLatch.countDown();
1522bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            }
1523bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1524bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            @Override
1525bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
1526bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                return true;
1527bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            }
1528bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        };
1529bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final DefaultItemAnimator animator = new DefaultItemAnimator();
1530bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        animator.setSupportsChangeAnimations(true);
1531bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        animator.setChangeDuration(10000);
1532bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        testAdapter.setHasStableIds(true);
1533bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final TestRecyclerView recyclerView = new TestRecyclerView(getActivity());
1534bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.setLayoutManager(tlm);
1535bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.setAdapter(testAdapter);
1536bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.setItemAnimator(animator);
1537bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1538bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.expectLayouts(1);
1539bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        setRecyclerView(recyclerView);
1540bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.waitForLayout(2);
1541bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1542bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.expectLayouts(2);
1543bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        testAdapter.mItems.get(2).mType += 2;
1544bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final int itemId = testAdapter.mItems.get(2).mId;
1545bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        testAdapter.changeAndNotify(2, 1);
1546bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.waitForLayout(2);
1547bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1548bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        runTestOnUiThread(new Runnable() {
1549bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            @Override
1550bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            public void run() {
1551bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat("test sanity", recyclerView.getChildCount(), CoreMatchers.is(11));
1552bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // now mangle the order and run the test
1553bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                RecyclerView.ViewHolder hidden = null;
1554bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                RecyclerView.ViewHolder updated = null;
1555bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                for (int i = 0; i < recyclerView.getChildCount(); i ++) {
1556bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    View view = recyclerView.getChildAt(i);
1557bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view);
1558bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    if (vh.getAdapterPosition() == 2) {
1559bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        if (mRecyclerView.mChildHelper.isHidden(view)) {
1560bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            assertThat(hidden, CoreMatchers.nullValue());
1561bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            hidden = vh;
1562bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        } else {
1563bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            assertThat(updated, CoreMatchers.nullValue());
1564bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            updated = vh;
1565bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        }
1566bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    }
1567bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                }
1568bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(hidden, CoreMatchers.notNullValue());
1569bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(updated, CoreMatchers.notNullValue());
1570bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1571bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                mRecyclerView.eatRequestLayout();
1572bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1573bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // first put the hidden child back
1574bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                int index1 = mRecyclerView.indexOfChild(hidden.itemView);
1575bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                int index2 = mRecyclerView.indexOfChild(updated.itemView);
1576bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                if (index1 < index2) {
1577bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    // swap views
1578bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    swapViewsAtIndices(recyclerView, index1, index2);
1579bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                }
1580bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(tlm.findViewByPosition(2), CoreMatchers.sameInstance(updated.itemView));
1581bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1582bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForAdapterPosition(2),
1583bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
1584bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForLayoutPosition(2),
1585bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
1586bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForItemId(itemId),
1587bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
1588bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1589bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // now swap back
1590bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                swapViewsAtIndices(recyclerView, index1, index2);
1591bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1592bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(tlm.findViewByPosition(2), CoreMatchers.sameInstance(updated.itemView));
1593bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForAdapterPosition(2),
1594bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
1595bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForLayoutPosition(2),
1596bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
1597bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForItemId(itemId),
1598bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
1599bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1600bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // now remove updated. re-assert fallback to the hidden one
1601bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                tlm.removeView(updated.itemView);
1602bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1603bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(tlm.findViewByPosition(2), CoreMatchers.nullValue());
1604bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForAdapterPosition(2),
1605bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(hidden));
1606bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForLayoutPosition(2),
1607bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(hidden));
1608bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForItemId(itemId),
1609bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(hidden));
1610bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            }
1611bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        });
1612bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1613bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    }
1614bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1615bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    private void swapViewsAtIndices(TestRecyclerView recyclerView, int index1, int index2) {
1616bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (index1 == index2) {
1617bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            return;
1618bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
1619bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (index2 < index1) {
1620bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            int tmp = index1;
1621bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            index1 = index2;
1622bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            index2 = tmp;
1623bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
1624bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final View v1 = recyclerView.getChildAt(index1);
1625bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final View v2 = recyclerView.getChildAt(index2);
1626bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        boolean v1Hidden = recyclerView.mChildHelper.isHidden(v1);
1627bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        boolean v2Hidden = recyclerView.mChildHelper.isHidden(v2);
1628bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        // must unhide before swap otherwise bucket indices will become invalid.
1629bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v1Hidden) {
1630bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.unhide(v1);
1631bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
1632bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v2Hidden) {
1633bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.unhide(v2);
1634bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
1635bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.detachViewFromParent(index2);
1636bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.attachViewToParent(v2, index1, v2.getLayoutParams());
1637bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.detachViewFromParent(index1 + 1);
1638bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.attachViewToParent(v1, index2, v1.getLayoutParams());
1639bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1640bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v1Hidden) {
1641bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.hide(v1);
1642bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
1643bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v2Hidden) {
1644bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.hide(v2);
1645bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
1646bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    }
1647bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
1648115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    public void adapterPositionsTest(final AdapterRunnable adapterChanges) throws Throwable {
1649115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
1650115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
1651115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1652115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1653115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                try {
1654115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    layoutRange(recycler, Math.min(state.getItemCount(), 2)
1655115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            , Math.min(state.getItemCount(), 7));
1656115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    layoutLatch.countDown();
1657115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                } catch (Throwable t) {
1658115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    postExceptionToInstrumentation(t);
1659115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                }
1660115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1661115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        };
1662115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
1663115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        recyclerView.setLayoutManager(tlm);
1664115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        recyclerView.setAdapter(testAdapter);
1665115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        tlm.expectLayouts(1);
1666115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        setRecyclerView(recyclerView);
1667115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        tlm.waitForLayout(1);
1668115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        runTestOnUiThread(new Runnable() {
1669115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
1670115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run() {
1671115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                try {
1672115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    final int count = recyclerView.getChildCount();
1673115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    Map<View, Integer> layoutPositions = new HashMap<View, Integer>();
1674115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    assertTrue("test sanity", count > 0);
16751a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    for (int i = 0; i < count; i++) {
1676115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        View view = recyclerView.getChildAt(i);
1677115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        TestViewHolder vh = (TestViewHolder) recyclerView.getChildViewHolder(view);
1678115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        int index = testAdapter.mItems.indexOf(vh.mBoundItem);
1679115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        assertEquals("should be able to find VH with adapter position " + index, vh,
1680115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                recyclerView.findViewHolderForAdapterPosition(index));
1681115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        assertEquals("get adapter position should return correct index", index,
1682115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                vh.getAdapterPosition());
1683115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        layoutPositions.put(view, vh.mPosition);
1684115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    }
1685115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    if (adapterChanges != null) {
1686115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        adapterChanges.run(testAdapter);
1687115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        for (int i = 0; i < count; i++) {
1688115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            View view = recyclerView.getChildAt(i);
1689115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            TestViewHolder vh = (TestViewHolder) recyclerView
1690115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    .getChildViewHolder(view);
1691115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            int index = testAdapter.mItems.indexOf(vh.mBoundItem);
1692115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            if (index >= 0) {
1693115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                assertEquals("should be able to find VH with adapter position "
16941a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                                                + index, vh,
1695115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                        recyclerView.findViewHolderForAdapterPosition(index));
1696115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            }
1697115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            assertSame("get adapter position should return correct index", index,
1698115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    vh.getAdapterPosition());
1699115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            assertSame("should be able to find view with layout position",
1700115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    vh, mRecyclerView.findViewHolderForLayoutPosition(
1701115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                            layoutPositions.get(view)));
1702115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        }
1703115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1704115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    }
1705115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
1706115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                } catch (Throwable t) {
1707115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    postExceptionToInstrumentation(t);
1708115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                }
1709115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
1710115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
1711115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        checkForMainThreadException();
1712e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
1713e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
1714be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForSmoothScroll() throws Throwable {
1716bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
1717bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
1718bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1719bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
1720bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
1721bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
1722bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
1723bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
1724bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
1725bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(2);
1726bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
1727bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1728bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
1729bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
1730bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
1731bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1732bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1733bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        runTestOnUiThread(new Runnable() {
1734bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1735bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
1736bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.smoothScrollBy(0, 500);
1737bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1738bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1739bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
1740bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
1741bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
1742bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
1743bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
1744bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1745be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForSmoothScrollWithStop() throws Throwable {
1747bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
1748bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
1749bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1750bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
1751bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
1752bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
1753bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
1754bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
1755bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
1756bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(1);
1757bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
1758bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1759bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
1760bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
1761bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
1762bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1763bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1764bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        runTestOnUiThread(new Runnable() {
1765bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1766bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
1767bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.smoothScrollBy(0, 500);
1768bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1769bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1770bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
1771bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        runTestOnUiThread(new Runnable() {
1772bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1773bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
1774bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.stopScroll();
1775bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1776bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1777bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
1778bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
1779bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
1780bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
1781bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
1782bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1783be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForFling() throws Throwable {
1785bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
1786bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
1787bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1788bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
1789bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
1790bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
1791bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
1792bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
1793bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
1794bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(2);
1795bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
1796bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1797bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
1798bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
1799bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
1800bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1801bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1802bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final ViewConfiguration vc = ViewConfiguration.get(getActivity());
1803bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final float fling = vc.getScaledMinimumFlingVelocity()
1804bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                + (vc.getScaledMaximumFlingVelocity() - vc.getScaledMinimumFlingVelocity()) * .1f;
1805bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        runTestOnUiThread(new Runnable() {
1806bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1807bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
1808bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.fling(0, Math.round(fling));
1809bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1810bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1811bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
1812bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
1813bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
1814bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
1815bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
1816bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1817be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
18180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForFlingWithStop() throws Throwable {
1819bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
1820bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
1821bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1822bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
1823bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
1824bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
1825bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
1826bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
1827bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
1828bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(1);
1829bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
1830bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1831bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
1832bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
1833bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
1834bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1835bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1836bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final ViewConfiguration vc = ViewConfiguration.get(getActivity());
1837bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final float fling = vc.getScaledMinimumFlingVelocity()
1838bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                + (vc.getScaledMaximumFlingVelocity() - vc.getScaledMinimumFlingVelocity()) * .8f;
1839bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        runTestOnUiThread(new Runnable() {
1840bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1841bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
1842bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.fling(0, Math.round(fling));
1843bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1844bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1845bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
1846bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        runTestOnUiThread(new Runnable() {
1847bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1848bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
1849bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.stopScroll();
1850bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1851bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1852bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
1853bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
1854bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
1855bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
1856bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
1857bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1858be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
18590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateDrag() throws Throwable {
1860bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
1861bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
1862bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1863bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
1864bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
1865bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
1866bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
1867bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
1868bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
1869bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
1870bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1871bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
1872bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
1873bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1874bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1875bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        drag(mRecyclerView, 0, 0, 0, 500, 5);
1876bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_SETTLING]);
1877bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
1878bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_DRAGGING]);
1879bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
1880bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1881bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    public void drag(ViewGroup view, float fromX, float toX, float fromY, float toY,
1882bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            int stepCount) throws Throwable {
1883bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        long downTime = SystemClock.uptimeMillis();
1884bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        long eventTime = SystemClock.uptimeMillis();
1885bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1886bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float y = fromY;
1887bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float x = fromX;
1888bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1889bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float yStep = (toY - fromY) / stepCount;
1890bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float xStep = (toX - fromX) / stepCount;
1891bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1892bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
1893bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                MotionEvent.ACTION_DOWN, x, y, 0);
1894bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        sendTouch(view, event);
1895bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        for (int i = 0; i < stepCount; ++i) {
1896bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            y += yStep;
1897bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            x += xStep;
1898bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            eventTime = SystemClock.uptimeMillis();
1899bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
1900bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            sendTouch(view, event);
1901bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
1902bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1903bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        eventTime = SystemClock.uptimeMillis();
1904bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
1905bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        sendTouch(view, event);
1906bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
1907bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
1908bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1909bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    private void sendTouch(final ViewGroup view, final MotionEvent event) throws Throwable {
1910bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        runTestOnUiThread(new Runnable() {
1911bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
1912bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
1913bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                if (view.onInterceptTouchEvent(event)) {
1914bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                    view.onTouchEvent(event);
1915bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                }
1916bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
1917bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
1918bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
1919bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
1920be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
19210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleScrap() throws Throwable {
192266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recycleScrapTest(false);
192366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        removeRecyclerView();
192466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recycleScrapTest(true);
192566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar    }
192666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar
192766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar    public void recycleScrapTest(final boolean useRecycler) throws Throwable {
192866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
192966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        final AtomicBoolean test = new AtomicBoolean(false);
193066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
193166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar            @Override
193266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1933e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                ViewInfoStore infoStore = mRecyclerView.mViewInfoStore;
193466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                if (test.get()) {
193566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                    try {
193666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        detachAndScrapAttachedViews(recycler);
193766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        for (int i = recycler.getScrapList().size() - 1; i >= 0; i--) {
193866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            if (useRecycler) {
193966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                recycler.recycleView(recycler.getScrapList().get(i).itemView);
194066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            } else {
194166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                removeAndRecycleView(recycler.getScrapList().get(i).itemView,
194266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                        recycler);
194366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            }
194466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        }
1945e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                        if (infoStore.mOldChangedHolders != null) {
1946e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                            for (int i = infoStore.mOldChangedHolders.size() - 1; i >= 0; i--) {
194766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                if (useRecycler) {
194866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                    recycler.recycleView(
1949e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                            infoStore.mOldChangedHolders.valueAt(i).itemView);
195066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                } else {
195166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                    removeAndRecycleView(
1952e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                            infoStore.mOldChangedHolders.valueAt(i).itemView,
1953e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                            recycler);
195466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                }
195566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            }
195666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        }
195766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        assertEquals("no scrap should be left over", 0, recycler.getScrapCount());
195866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        assertEquals("pre layout map should be empty", 0,
1959e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                InfoStoreTrojan.sizeOfPreLayout(infoStore));
196066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        assertEquals("post layout map should be empty", 0,
1961e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                InfoStoreTrojan.sizeOfPostLayout(infoStore));
1962e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                        if (infoStore.mOldChangedHolders != null) {
196366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            assertEquals("post old change map should be empty", 0,
1964e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                    infoStore.mOldChangedHolders.size());
196566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        }
196666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                    } catch (Throwable t) {
196766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        postExceptionToInstrumentation(t);
196866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                    }
196966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar
197066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                }
197166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                layoutRange(recycler, 0, 5);
197266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                layoutLatch.countDown();
197366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                super.onLayoutChildren(recycler, state);
197466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar            }
197566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        };
197666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
197766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recyclerView.setAdapter(testAdapter);
197866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recyclerView.setLayoutManager(lm);
1979121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        ((SimpleItemAnimator)recyclerView.getItemAnimator()).setSupportsChangeAnimations(true);
198066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.expectLayouts(1);
198166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        setRecyclerView(recyclerView);
198266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.waitForLayout(2);
198366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        test.set(true);
198466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.expectLayouts(1);
198566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        testAdapter.changeAndNotify(3, 1);
198666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.waitForLayout(2);
198766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        checkForMainThreadException();
198866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar    }
198922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
1990be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
19914143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    public void aAccessRecyclerOnOnMeasureWithPredictive() throws Throwable {
19925cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        accessRecyclerOnOnMeasureTest(true);
19935cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar    }
19945cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
1995be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
19964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    public void accessRecyclerOnOnMeasureWithoutPredictive() throws Throwable {
19974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        accessRecyclerOnOnMeasureTest(false);
19984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
19994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
20004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    @Test
20010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void smoothScrollWithRemovedItemsAndRemoveItem() throws Throwable {
2002cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        smoothScrollTest(true);
2003cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    }
2004cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar
2005cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    @Test
20060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void smoothScrollWithRemovedItems() throws Throwable {
2007ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        smoothScrollTest(false);
2008ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    }
2009ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2010ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    public void smoothScrollTest(final boolean removeItem) throws Throwable {
2011ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final LinearSmoothScroller[] lss = new LinearSmoothScroller[1];
2012ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final CountDownLatch calledOnStart = new CountDownLatch(1);
2013ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final CountDownLatch calledOnStop = new CountDownLatch(1);
2014ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final int visibleChildCount = 10;
2015ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
2016ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            int start = 0;
2017ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2018ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2019ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2020ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                super.onLayoutChildren(recycler, state);
2021ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                layoutRange(recycler, start, visibleChildCount);
2022ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                layoutLatch.countDown();
2023ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2024ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2025ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2026ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
2027ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    RecyclerView.State state) {
2028ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                start++;
2029ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                if (DEBUG) {
2030ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    Log.d(TAG, "on scroll, remove and recycling. start:" + start + ", cnt:"
2031ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            + visibleChildCount);
2032ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                }
2033ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                removeAndRecycleAllViews(recycler);
2034c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                layoutRange(recycler, start,
2035c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        Math.max(state.getItemCount(), start + visibleChildCount));
2036ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                return dy;
2037ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2038ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2039ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2040ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public boolean canScrollVertically() {
2041ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                return true;
2042ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2043ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2044ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2045ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
2046ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    int position) {
2047ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                LinearSmoothScroller linearSmoothScroller =
2048ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        new LinearSmoothScroller(recyclerView.getContext()) {
2049ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            @Override
2050ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            public PointF computeScrollVectorForPosition(int targetPosition) {
2051ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                return new PointF(0, 1);
2052ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
2053ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2054ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            @Override
2055ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            protected void onStart() {
2056ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                super.onStart();
2057ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                calledOnStart.countDown();
2058ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
2059ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2060ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            @Override
2061ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            protected void onStop() {
2062ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                super.onStop();
2063ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                calledOnStop.countDown();
2064ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
2065ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        };
2066ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                linearSmoothScroller.setTargetPosition(position);
2067ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                lss[0] = linearSmoothScroller;
2068ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                startSmoothScroll(linearSmoothScroller);
2069ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2070ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        };
2071ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
2072ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        TestAdapter testAdapter = new TestAdapter(500);
2073ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        rv.setLayoutManager(lm);
2074ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        rv.setAdapter(testAdapter);
2075ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        lm.expectLayouts(1);
2076ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        setRecyclerView(rv);
2077ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        lm.waitForLayout(1);
2078ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        // regular scroll
2079ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final int targetPosition = visibleChildCount * (removeItem ? 30 : 4);
2080ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        runTestOnUiThread(new Runnable() {
2081ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2082ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public void run() {
2083ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                rv.smoothScrollToPosition(targetPosition);
2084ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2085ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        });
2086ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        if (DEBUG) {
2087ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            Log.d(TAG, "scrolling to target position " + targetPosition);
2088ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        }
2089ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        assertTrue("on start should be called very soon", calledOnStart.await(2, TimeUnit.SECONDS));
2090ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        if (removeItem) {
2091ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            final int newTarget = targetPosition - 10;
2092ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            testAdapter.deleteAndNotify(newTarget + 1, testAdapter.getItemCount() - newTarget - 1);
209390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            final CountDownLatch targetCheck = new CountDownLatch(1);
2094ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            runTestOnUiThread(new Runnable() {
2095ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                @Override
2096ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                public void run() {
2097ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    ViewCompat.postOnAnimationDelayed(rv, new Runnable() {
2098ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        @Override
2099ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        public void run() {
2100ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            try {
2101ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                assertEquals("scroll position should be updated to next available",
2102ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                        newTarget, lss[0].getTargetPosition());
2103ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            } catch (Throwable t) {
2104ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                postExceptionToInstrumentation(t);
2105ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
210690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                            targetCheck.countDown();
2107ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        }
210890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    }, 50);
2109ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                }
2110ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            });
211190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            assertTrue("target position should be checked on time ",
211290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    targetCheck.await(10, TimeUnit.SECONDS));
2113ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            checkForMainThreadException();
2114ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertTrue("on stop should be called", calledOnStop.await(30, TimeUnit.SECONDS));
2115ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            checkForMainThreadException();
2116ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertNotNull("should scroll to new target " + newTarget
2117115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    , rv.findViewHolderForLayoutPosition(newTarget));
2118ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            if (DEBUG) {
2119ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                Log.d(TAG, "on stop has been called on time");
2120ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2121ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        } else {
2122ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertTrue("on stop should be called eventually",
2123ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    calledOnStop.await(30, TimeUnit.SECONDS));
2124ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertNotNull("scroll to position should succeed",
2125115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    rv.findViewHolderForLayoutPosition(targetPosition));
2126ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        }
2127ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        checkForMainThreadException();
2128ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    }
2129ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2130be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
21310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void consecutiveSmoothScroll() throws Throwable {
213278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final AtomicInteger visibleChildCount = new AtomicInteger(10);
213378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final AtomicInteger totalScrolled = new AtomicInteger(0);
213478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
213578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            int start = 0;
213678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
213778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
213878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
213978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                super.onLayoutChildren(recycler, state);
214078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                layoutRange(recycler, start, visibleChildCount.get());
214178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                layoutLatch.countDown();
214278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
214378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
214478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
214578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
214678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                    RecyclerView.State state) {
214778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                totalScrolled.set(totalScrolled.get() + dy);
214878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                return dy;
214978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
215078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
215178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
215278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public boolean canScrollVertically() {
215378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                return true;
215478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
215578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        };
215678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
215778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        TestAdapter testAdapter = new TestAdapter(500);
215878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        rv.setLayoutManager(lm);
215978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        rv.setAdapter(testAdapter);
216078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        lm.expectLayouts(1);
216178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        setRecyclerView(rv);
216278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        lm.waitForLayout(1);
216378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        runTestOnUiThread(new Runnable() {
216478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
216578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public void run() {
216678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                rv.smoothScrollBy(0, 2000);
216778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
216878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        });
216978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        Thread.sleep(250);
217078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final AtomicInteger scrollAmt = new AtomicInteger();
217178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        runTestOnUiThread(new Runnable() {
217278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
217378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public void run() {
217478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                final int soFar = totalScrolled.get();
217578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                scrollAmt.set(soFar);
217678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                rv.smoothScrollBy(0, 5000 - soFar);
217778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
217878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        });
21791a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        while (rv.getScrollState() != SCROLL_STATE_IDLE) {
218078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            Thread.sleep(100);
218178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        }
218278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final int soFar = totalScrolled.get();
218378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        assertEquals("second scroll should be competed properly", 5000, soFar);
218478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar    }
218578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
21865cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar    public void accessRecyclerOnOnMeasureTest(final boolean enablePredictiveAnimations)
21875cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            throws Throwable {
21885cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
21895cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        final AtomicInteger expectedOnMeasureStateCount = new AtomicInteger(10);
21905cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
21915cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            @Override
21925cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
21935cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                super.onLayoutChildren(recycler, state);
21945cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                try {
21955cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
21965cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    layoutLatch.countDown();
21975cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                } catch (Throwable t) {
21985cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    postExceptionToInstrumentation(t);
21995cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                } finally {
22005cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    layoutLatch.countDown();
22015cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                }
22025cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            }
22035cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
22045cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            @Override
22055cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
22065cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    int widthSpec, int heightSpec) {
22075cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                try {
22085cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    // make sure we access all views
22095cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    for (int i = 0; i < state.getItemCount(); i++) {
22105cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        View view = recycler.getViewForPosition(i);
22115cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        assertNotNull(view);
22125cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        assertEquals(i, getPosition(view));
22135cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    }
22144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    if (!state.isPreLayout()) {
22154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        assertEquals(state.toString(),
22164143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                                expectedOnMeasureStateCount.get(), state.getItemCount());
22174143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    }
2218824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                } catch (Throwable t) {
22195cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    postExceptionToInstrumentation(t);
22205cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                }
22215cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                super.onMeasure(recycler, state, widthSpec, heightSpec);
22225cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            }
22235cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
22245cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            @Override
22255cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            public boolean supportsPredictiveItemAnimations() {
22265cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                return enablePredictiveAnimations;
22275cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            }
22285cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        };
22295cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
22305cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        recyclerView.setLayoutManager(lm);
22315cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        recyclerView.setAdapter(testAdapter);
22325cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        recyclerView.setLayoutManager(lm);
22335cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.expectLayouts(1);
22345cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        setRecyclerView(recyclerView);
22355cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.waitForLayout(2);
22365cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        checkForMainThreadException();
22375cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.expectLayouts(1);
22385cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        if (!enablePredictiveAnimations) {
22395cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            expectedOnMeasureStateCount.set(15);
22405cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        }
22415cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        testAdapter.addAndNotify(4, 5);
22425cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.waitForLayout(2);
22435cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        checkForMainThreadException();
22445cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar    }
22455cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
2246be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
22470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void setCompatibleAdapter() throws Throwable {
22480b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(true, true);
22490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
22500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(false, true);
22510b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
22520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(true, false);
22530b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
22540b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(false, false);
22550b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
22560b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
22570b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
22580b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    private void compatibleAdapterTest(boolean useCustomPool, boolean removeAndRecycleExistingViews)
22590b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            throws Throwable {
22600b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
22610b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        final AtomicInteger recycledViewCount = new AtomicInteger();
22620b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
22630b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
22640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
22650b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                try {
22660b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
22670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
22680b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } catch (Throwable t) {
22690b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    postExceptionToInstrumentation(t);
22700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } finally {
22710b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
22720b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                }
22730b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
22740b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        };
22750b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
22760b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setLayoutManager(lm);
22770b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setAdapter(testAdapter);
22780b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setRecyclerListener(new RecyclerView.RecyclerListener() {
22790b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
22800b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            public void onViewRecycled(RecyclerView.ViewHolder holder) {
22810b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                recycledViewCount.incrementAndGet();
22820b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
22830b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        });
22840b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
22850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        setRecyclerView(recyclerView, !useCustomPool);
22860b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
22870b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
22880b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
22890b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        swapAdapter(new TestAdapter(10), removeAndRecycleExistingViews);
22900b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
22910b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
22920b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        if (removeAndRecycleExistingViews) {
22930b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            assertTrue("Previous views should be recycled", recycledViewCount.get() > 0);
22940b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        } else {
22950b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            assertEquals("No views should be recycled if adapters are compatible and developer "
22960b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    + "did not request a recycle", 0, recycledViewCount.get());
22970b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
22980b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
22990b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
2300be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
23010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void setIncompatibleAdapter() throws Throwable {
23020b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        incompatibleAdapterTest(true);
23030b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        incompatibleAdapterTest(false);
23040b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
23050b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
23060b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    public void incompatibleAdapterTest(boolean useCustomPool) throws Throwable {
23070b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
23080b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
23090b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
23100b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
23110b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                super.onLayoutChildren(recycler, state);
23120b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                try {
23130b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
23140b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
23150b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } catch (Throwable t) {
23160b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    postExceptionToInstrumentation(t);
23170b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } finally {
23180b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
23190b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                }
23200b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
23210b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        };
23220b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
23230b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setLayoutManager(lm);
23240b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setAdapter(testAdapter);
23250b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setLayoutManager(lm);
23260b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
23270b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        setRecyclerView(recyclerView, !useCustomPool);
23280b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
23290b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
23300b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
23310b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        setAdapter(new TestAdapter2(10));
23320b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
23330b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
23340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
23350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
2336be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
23370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleIgnored() throws Throwable {
23386e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        final TestAdapter adapter = new TestAdapter(10);
23396e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
23406e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            @Override
23416e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
23426e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                layoutRange(recycler, 0, 5);
23436e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                layoutLatch.countDown();
23446e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            }
23456e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        };
23466e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
23476e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        recyclerView.setAdapter(adapter);
23486e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        recyclerView.setLayoutManager(lm);
23496e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        lm.expectLayouts(1);
23506e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        setRecyclerView(recyclerView);
23516e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        lm.waitForLayout(2);
23526e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        runTestOnUiThread(new Runnable() {
23536e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            @Override
23546e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            public void run() {
23556e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                View child1 = lm.findViewByPosition(0);
23566e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                View child2 = lm.findViewByPosition(1);
23576e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.ignoreView(child1);
23586e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.ignoreView(child2);
23596e560171b774afafd572b1896becac7b308a92e6Yigit Boyar
23606e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.removeAndRecycleAllViews(recyclerView.mRecycler);
23616e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                assertEquals("ignored child should not be recycled or removed", 2,
23626e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                        lm.getChildCount());
23636e560171b774afafd572b1896becac7b308a92e6Yigit Boyar
23646e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                Throwable[] throwables = new Throwable[1];
23656e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                try {
23666e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                    lm.removeAndRecycleView(child1, mRecyclerView.mRecycler);
23676e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                } catch (Throwable t) {
23686e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                    throwables[0] = t;
23696e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                }
23706e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                assertTrue("Trying to recycle an ignored view should throw IllegalArgException "
23715cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        , throwables[0] instanceof IllegalArgumentException);
23726e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.removeAllViews();
23736e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                assertEquals("ignored child should be removed as well ", 0, lm.getChildCount());
23746e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            }
23756e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        });
23766e560171b774afafd572b1896becac7b308a92e6Yigit Boyar    }
23776e560171b774afafd572b1896becac7b308a92e6Yigit Boyar
2378be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
23790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void findIgnoredByPosition() throws Throwable {
2380f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final TestAdapter adapter = new TestAdapter(10);
2381f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
2382f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            @Override
2383f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2384f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                detachAndScrapAttachedViews(recycler);
2385f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                layoutRange(recycler, 0, 5);
2386f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                layoutLatch.countDown();
2387f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            }
2388f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        };
2389f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
2390f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        recyclerView.setAdapter(adapter);
2391f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        recyclerView.setLayoutManager(lm);
2392f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.expectLayouts(1);
2393f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        setRecyclerView(recyclerView);
2394f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.waitForLayout(2);
2395f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        Thread.sleep(5000);
2396f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final int pos = 1;
2397f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final View[] ignored = new View[1];
2398f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        runTestOnUiThread(new Runnable() {
2399f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            @Override
2400f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            public void run() {
2401f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                View child = lm.findViewByPosition(pos);
2402f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                lm.ignoreView(child);
2403f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                ignored[0] = child;
2404f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            }
2405f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        });
2406f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNotNull("ignored child should not be null", ignored[0]);
2407f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNull("find view by position should not return ignored child",
2408f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                lm.findViewByPosition(pos));
2409f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.expectLayouts(1);
2410f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        requestLayoutOnUIThread(mRecyclerView);
2411f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.waitForLayout(1);
2412f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertEquals("child count should be ", 6, lm.getChildCount());
2413f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        View replacement = lm.findViewByPosition(pos);
2414f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNotNull("re-layout should replace ignored child w/ another one", replacement);
2415f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNotSame("replacement should be a different view", replacement, ignored[0]);
2416f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar    }
2417f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar
2418be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
24190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void invalidateAllDecorOffsets() throws Throwable {
2420bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final TestAdapter adapter = new TestAdapter(10);
2421bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
2422bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final AtomicBoolean invalidatedOffsets = new AtomicBoolean(true);
2423bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        recyclerView.setAdapter(adapter);
2424bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final AtomicInteger layoutCount = new AtomicInteger(4);
2425bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final RecyclerView.ItemDecoration dummyItemDecoration = new RecyclerView.ItemDecoration() {
2426bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        };
2427bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        TestLayoutManager testLayoutManager = new TestLayoutManager() {
2428bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
2429bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2430bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                try {
2431bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    // test
2432824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    for (int i = 0; i < getChildCount(); i++) {
2433bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        View child = getChildAt(i);
2434bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)
2435bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                child.getLayoutParams();
2436bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        assertEquals(
2437bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                "Decor insets validation for VH should have expected value.",
2438bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                invalidatedOffsets.get(), lp.mInsetsDirty);
2439bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    }
2440bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    for (RecyclerView.ViewHolder vh : mRecyclerView.mRecycler.mCachedViews) {
2441bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)
2442bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                vh.itemView.getLayoutParams();
2443bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        assertEquals(
2444bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                "Decor insets invalidation in cache for VH should have expected "
2445bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                        + "value.",
2446bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                invalidatedOffsets.get(), lp.mInsetsDirty);
2447bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    }
2448bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    detachAndScrapAttachedViews(recycler);
2449bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    layoutRange(recycler, 0, layoutCount.get());
2450bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                } catch (Throwable t) {
2451bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    postExceptionToInstrumentation(t);
2452bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                } finally {
2453bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    layoutLatch.countDown();
2454bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                }
2455bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
2456bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2457bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
2458bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
2459bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                return false;
2460bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
2461bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        };
2462bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // first layout
2463bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        recyclerView.setItemViewCacheSize(5);
2464bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        recyclerView.setLayoutManager(testLayoutManager);
2465bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2466e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        setRecyclerView(recyclerView, true, false);
2467bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(2);
2468bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2469bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2470bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // re-layout w/o any change
2471bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(false);
2472bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2473bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        requestLayoutOnUIThread(recyclerView);
2474bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
2475bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2476bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2477bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // invalidate w/o an item decorator
2478e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar
2479bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
2480bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2481bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
2482bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.assertNoLayout("layout should not happen", 2);
2483bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2484bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2485bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // set item decorator, should invalidate
2486bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
2487bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2488bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        addItemDecoration(mRecyclerView, dummyItemDecoration);
2489bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
2490bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2491bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2492bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // re-layout w/o any change
2493bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(false);
2494bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2495bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        requestLayoutOnUIThread(recyclerView);
2496bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
2497bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2498bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2499bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // invalidate w/ item decorator
2500bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
2501bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
2502bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2503bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
2504bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(2);
2505bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2506bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2507bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // trigger cache.
2508bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        layoutCount.set(3);
2509bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(false);
2510bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2511bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        requestLayoutOnUIThread(mRecyclerView);
2512bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
2513bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2514bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        assertEquals("a view should be cached", 1, mRecyclerView.mRecycler.mCachedViews.size());
2515bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2516bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        layoutCount.set(5);
2517bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
2518bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2519bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
2520bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
2521bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2522bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2523bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // remove item decorator
2524bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
2525bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
2526bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        removeItemDecoration(mRecyclerView, dummyItemDecoration);
2527bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
2528bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
2529bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
2530bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2531bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    public void addItemDecoration(final RecyclerView recyclerView, final
2532824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    RecyclerView.ItemDecoration itemDecoration) throws Throwable {
2533bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        runTestOnUiThread(new Runnable() {
2534bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
2535bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void run() {
2536bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                recyclerView.addItemDecoration(itemDecoration);
2537bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
2538bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        });
2539bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
2540bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2541bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    public void removeItemDecoration(final RecyclerView recyclerView, final
2542bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    RecyclerView.ItemDecoration itemDecoration) throws Throwable {
2543bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        runTestOnUiThread(new Runnable() {
2544bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
2545bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void run() {
2546bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                recyclerView.removeItemDecoration(itemDecoration);
2547bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
2548bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        });
2549bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
2550bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2551bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    public void invalidateDecorOffsets(final RecyclerView recyclerView) throws Throwable {
2552bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        runTestOnUiThread(new Runnable() {
2553bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
2554bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void run() {
2555bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                recyclerView.invalidateItemDecorations();
2556bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
2557bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        });
2558bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
2559bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
2560be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
25610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void invalidateDecorOffsets() throws Throwable {
2562cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        final TestAdapter adapter = new TestAdapter(10);
2563cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.setHasStableIds(true);
2564cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        final RecyclerView recyclerView = new RecyclerView(getActivity());
2565cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        recyclerView.setAdapter(adapter);
2566cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
2567cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        final Map<Long, Boolean> changes = new HashMap<Long, Boolean>();
2568cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
2569cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        TestLayoutManager testLayoutManager = new TestLayoutManager() {
2570cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            @Override
2571cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2572cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                try {
2573cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    if (changes.size() > 0) {
2574cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                        // test
2575824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                        for (int i = 0; i < getChildCount(); i++) {
2576cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            View child = getChildAt(i);
2577cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)
2578cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                                    child.getLayoutParams();
2579cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            RecyclerView.ViewHolder vh = lp.mViewHolder;
2580cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            if (!changes.containsKey(vh.getItemId())) {
2581cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                                continue; //nothing to test
2582cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            }
2583cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            assertEquals(
2584cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                                    "Decord insets validation for VH should have expected value.",
2585cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                                    changes.get(vh.getItemId()).booleanValue(),
2586cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                                    lp.mInsetsDirty);
2587cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                        }
2588cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    }
2589cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    detachAndScrapAttachedViews(recycler);
2590cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    layoutRange(recycler, 0, state.getItemCount());
2591cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                } catch (Throwable t) {
2592cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    postExceptionToInstrumentation(t);
2593cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                } finally {
2594cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    layoutLatch.countDown();
2595cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                }
2596cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            }
2597cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
2598cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            @Override
2599cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            public boolean supportsPredictiveItemAnimations() {
2600cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                return false;
2601cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            }
2602cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        };
2603cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        recyclerView.setLayoutManager(testLayoutManager);
2604cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
2605cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        setRecyclerView(recyclerView);
2606cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
2607cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        int itemAddedTo = 5;
2608cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        for (int i = 0; i < itemAddedTo; i++) {
2609115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), false);
2610cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
2611cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        for (int i = itemAddedTo; i < mRecyclerView.getChildCount(); i++) {
2612115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), true);
2613cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
2614cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
2615cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.addAndNotify(5, 1);
2616cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
2617cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        checkForMainThreadException();
2618cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
2619cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        changes.clear();
2620cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        int[] changedItems = new int[]{3, 5, 6};
2621824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        for (int i = 0; i < adapter.getItemCount(); i++) {
2622115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), false);
2623cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
2624824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        for (int i = 0; i < changedItems.length; i++) {
26251a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(changedItems[i]).getItemId(),
26261a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    true);
2627cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
2628cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
2629cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.changePositionsAndNotify(changedItems);
2630cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
2631cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        checkForMainThreadException();
2632cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
2633824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        for (int i = 0; i < adapter.getItemCount(); i++) {
2634115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), true);
2635cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
2636cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
2637cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.dispatchDataSetChanged();
2638cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
2639cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        checkForMainThreadException();
2640cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase    }
2641cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
2642be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
26430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void movingViaStableIds() throws Throwable {
2644c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        stableIdsMoveTest(true);
2645c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        removeRecyclerView();
2646c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        stableIdsMoveTest(false);
2647c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        removeRecyclerView();
2648c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar    }
2649c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
2650c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar    public void stableIdsMoveTest(final boolean supportsPredictive) throws Throwable {
2651c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
2652c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        testAdapter.setHasStableIds(true);
2653c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final AtomicBoolean test = new AtomicBoolean(false);
2654c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final int movedViewFromIndex = 3;
2655c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final int movedViewToIndex = 6;
2656c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final View[] movedView = new View[1];
2657c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
2658c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            @Override
2659c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2660c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                detachAndScrapAttachedViews(recycler);
2661c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                try {
2662c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    if (test.get()) {
2663c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                        if (state.isPreLayout()) {
2664c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            View view = recycler.getViewForPosition(movedViewFromIndex, true);
2665c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertSame("In pre layout, should be able to get moved view w/ old "
2666c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                                    + "position", movedView[0], view);
2667c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);
2668c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertTrue("it should come from scrap", holder.wasReturnedFromScrap());
2669c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            // clear scrap flag
2670c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            holder.clearReturnedFromScrapFlag();
2671c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                        } else {
2672c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            View view = recycler.getViewForPosition(movedViewToIndex, true);
2673c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertSame("In post layout, should be able to get moved view w/ new "
2674c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                                    + "position", movedView[0], view);
2675c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);
2676c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertTrue("it should come from scrap", holder.wasReturnedFromScrap());
2677c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            // clear scrap flag
2678c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            holder.clearReturnedFromScrapFlag();
2679c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                        }
2680c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    }
2681c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
2682c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                } catch (Throwable t) {
2683c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    postExceptionToInstrumentation(t);
2684c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                } finally {
2685c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    layoutLatch.countDown();
2686c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                }
2687c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
2688c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
2689c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            }
2690c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
2691c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            @Override
2692c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            public boolean supportsPredictiveItemAnimations() {
2693c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                return supportsPredictive;
2694c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            }
2695c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        };
2696c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        RecyclerView recyclerView = new RecyclerView(this.getActivity());
2697c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        recyclerView.setAdapter(testAdapter);
2698c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        recyclerView.setLayoutManager(lm);
2699c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.expectLayouts(1);
2700c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        setRecyclerView(recyclerView);
2701c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.waitForLayout(1);
2702c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
2703c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        movedView[0] = recyclerView.getChildAt(movedViewFromIndex);
2704c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        test.set(true);
2705c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.expectLayouts(supportsPredictive ? 2 : 1);
2706c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        runTestOnUiThread(new Runnable() {
2707c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            @Override
2708c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            public void run() {
2709c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                Item item = testAdapter.mItems.remove(movedViewFromIndex);
2710c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                testAdapter.mItems.add(movedViewToIndex, item);
2711c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                testAdapter.notifyItemRemoved(movedViewFromIndex);
2712c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                testAdapter.notifyItemInserted(movedViewToIndex);
2713c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            }
2714c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        });
2715c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.waitForLayout(2);
2716c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        checkForMainThreadException();
2717c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar    }
2718cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
2719be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
27200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterChangeDuringLayout() throws Throwable {
27210bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyDataSetChanged", new Runnable() {
27220bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
27230bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
27240bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyDataSetChanged();
27250bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
27260bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
27270bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
27280bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyItemChanged", new Runnable() {
27290bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
27300bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
27310bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyItemChanged(2);
27320bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
27330bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
27340bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
27350bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyItemInserted", new Runnable() {
27360bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
27370bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
27380bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyItemInserted(2);
27390bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
27400bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
27410bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyItemRemoved", new Runnable() {
27420bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
27430bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
27440bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyItemRemoved(2);
27450bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
27460bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
27470bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
27480bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
27490bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    public void adapterChangeInMainThreadTest(String msg,
27500bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            final Runnable onLayoutRunnable) throws Throwable {
275166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        setIgnoreMainThreadException(true);
27520bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        final AtomicBoolean doneFirstLayout = new AtomicBoolean(false);
27530bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
27540bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
27550bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
27560bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
27570bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                super.onLayoutChildren(recycler, state);
27580bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                try {
27590bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
27600bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    if (doneFirstLayout.get()) {
27610bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        onLayoutRunnable.run();
27620bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    }
27630bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } catch (Throwable t) {
27640bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    postExceptionToInstrumentation(t);
27650bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } finally {
27660bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutLatch.countDown();
27670bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
27680bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
27690bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
27700bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        };
27710bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
27720bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setLayoutManager(lm);
27730bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setAdapter(testAdapter);
27740bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
27750bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        setRecyclerView(recyclerView);
27760bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
27770bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        doneFirstLayout.set(true);
27780bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
27790bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        requestLayoutOnUIThread(recyclerView);
27800bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
27810bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        removeRecyclerView();
27820bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        assertTrue("Invalid data updates should be caught:" + msg,
278366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                getMainThreadException() instanceof IllegalStateException);
27840bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
27850bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
2786be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
27870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterChangeDuringScroll() throws Throwable {
27880bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        for (int orientation : new int[]{OrientationHelper.HORIZONTAL,
27890bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                OrientationHelper.VERTICAL}) {
27900bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyDataSetChanged", orientation,
27910bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    new Runnable() {
27920bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        @Override
27930bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        public void run() {
27940bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                            mRecyclerView.getAdapter().notifyDataSetChanged();
27950bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        }
27960bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    });
27970bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyItemChanged", orientation, new Runnable() {
27980bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                @Override
27990bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                public void run() {
28000bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    mRecyclerView.getAdapter().notifyItemChanged(2);
28010bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
28020bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            });
28030bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
28040bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyItemInserted", orientation, new Runnable() {
28050bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                @Override
28060bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                public void run() {
28070bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    mRecyclerView.getAdapter().notifyItemInserted(2);
28080bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
28090bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            });
28100bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyItemRemoved", orientation, new Runnable() {
28110bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                @Override
28120bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                public void run() {
28130bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    mRecyclerView.getAdapter().notifyItemRemoved(2);
28140bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
28150bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            });
28160bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        }
28170bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
28180bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
28190bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    public void adapterChangeDuringScrollTest(String msg, final int orientation,
28200bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            final Runnable onScrollRunnable) throws Throwable {
282166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        setIgnoreMainThreadException(true);
28220bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestAdapter testAdapter = new TestAdapter(100);
28230bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
28240bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
28250bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
28260bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                super.onLayoutChildren(recycler, state);
28270bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                try {
28280bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutRange(recycler, 0, 10);
28290bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } catch (Throwable t) {
28300bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    postExceptionToInstrumentation(t);
28310bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } finally {
28320bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutLatch.countDown();
28330bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
28340bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
28350bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
28360bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
28370bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public boolean canScrollVertically() {
28380bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return orientation == OrientationHelper.VERTICAL;
28390bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
28400bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
28410bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
28420bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public boolean canScrollHorizontally() {
28430bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return orientation == OrientationHelper.HORIZONTAL;
28440bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
28450bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
28460bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public int mockScroll() {
28470bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                try {
28480bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    onScrollRunnable.run();
28490bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } catch (Throwable t) {
28500bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    postExceptionToInstrumentation(t);
28510bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } finally {
28520bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutLatch.countDown();
28530bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
28540bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return 0;
28550bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
28560bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
28570bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
28580bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
28590bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    RecyclerView.State state) {
28600bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return mockScroll();
28610bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
28620bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
28630bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
28640bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
28650bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    RecyclerView.State state) {
28660bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return mockScroll();
28670bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
28680bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        };
28690bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
28700bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setLayoutManager(lm);
28710bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setAdapter(testAdapter);
28720bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
28730bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        setRecyclerView(recyclerView);
28740bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
28750bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
28760bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        scrollBy(200);
28770bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
28780bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        removeRecyclerView();
28790bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        assertTrue("Invalid data updates should be caught:" + msg,
288066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                getMainThreadException() instanceof IllegalStateException);
28810bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
28820bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
2883be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
28840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleOnDetach() throws Throwable {
288549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
288649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
288749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final AtomicBoolean didRunOnDetach = new AtomicBoolean(false);
288849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
288949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
289049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
289149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                super.onLayoutChildren(recycler, state);
289249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                layoutRange(recycler, 0, state.getItemCount() - 1);
289349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                layoutLatch.countDown();
289449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
289549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
289649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
289749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
289849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                super.onDetachedFromWindow(view, recycler);
289949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                didRunOnDetach.set(true);
290049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                removeAndRecycleAllViews(recycler);
290149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
290249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        };
290349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        recyclerView.setAdapter(testAdapter);
290449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        recyclerView.setLayoutManager(lm);
290549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        lm.expectLayouts(1);
290649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setRecyclerView(recyclerView);
290749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        lm.waitForLayout(2);
290849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        removeRecyclerView();
290949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        assertTrue("When recycler view is removed, detach should run", didRunOnDetach.get());
291049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        assertEquals("All children should be recycled", recyclerView.getChildCount(), 0);
291149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
29120bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
2913be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
29140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void updatesWhileDetached() throws Throwable {
2915bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
2916bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final int initialAdapterSize = 20;
2917bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final TestAdapter adapter = new TestAdapter(initialAdapterSize);
2918bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final AtomicInteger layoutCount = new AtomicInteger(0);
2919bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
2920bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            @Override
2921bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2922bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                super.onLayoutChildren(recycler, state);
2923bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutRange(recycler, 0, 5);
2924bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutCount.incrementAndGet();
2925bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutLatch.countDown();
2926bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            }
2927bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        };
2928bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setAdapter(adapter);
2929bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setLayoutManager(lm);
2930bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setHasFixedSize(true);
2931bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.expectLayouts(1);
2932bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        adapter.addAndNotify(4, 5);
2933bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.assertNoLayout("When RV is not attached, layout should not happen", 1);
2934bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar    }
2935bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar
2936be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
29370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void updatesAfterDetach() throws Throwable {
2938bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
2939bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final int initialAdapterSize = 20;
2940bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final TestAdapter adapter = new TestAdapter(initialAdapterSize);
2941bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final AtomicInteger layoutCount = new AtomicInteger(0);
2942bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
2943bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            @Override
2944bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2945bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                super.onLayoutChildren(recycler, state);
2946bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutRange(recycler, 0, 5);
2947bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutCount.incrementAndGet();
2948bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutLatch.countDown();
2949bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            }
2950bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        };
2951bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setAdapter(adapter);
2952bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setLayoutManager(lm);
2953bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.expectLayouts(1);
2954bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setHasFixedSize(true);
2955bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        setRecyclerView(recyclerView);
2956bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.waitForLayout(2);
2957bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.expectLayouts(1);
2958bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final int prevLayoutCount = layoutCount.get();
2959bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        runTestOnUiThread(new Runnable() {
2960bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            @Override
2961bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            public void run() {
2962bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                try {
2963bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                    adapter.addAndNotify(4, 5);
2964bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                    removeRecyclerView();
2965bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                } catch (Throwable throwable) {
296690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    postExceptionToInstrumentation(throwable);
2967bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                }
2968bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            }
2969bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        });
297090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        checkForMainThreadException();
2971bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar
2972bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.assertNoLayout("When RV is not attached, layout should not happen", 1);
2973bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        assertEquals("No extra layout should happen when detached", prevLayoutCount,
2974bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutCount.get());
2975bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar    }
297649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
2977be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
29780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void notifyDataSetChangedWithStableIds() throws Throwable {
2979e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final int defaultViewType = 1;
2980e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final Map<Item, Integer> viewTypeMap = new HashMap<Item, Integer>();
2981e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final Map<Integer, Integer> oldPositionToNewPositionMapping =
2982e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                new HashMap<Integer, Integer>();
2983e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final TestAdapter adapter = new TestAdapter(100) {
2984e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
2985e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public int getItemViewType(int position) {
2986e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                Integer type = viewTypeMap.get(mItems.get(position));
2987e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                return type == null ? defaultViewType : type;
2988e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
2989e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
2990e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
2991e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public long getItemId(int position) {
2992e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                return mItems.get(position).mId;
2993e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
2994e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        };
2995e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        adapter.setHasStableIds(true);
2996e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final ArrayList<Item> previousItems = new ArrayList<Item>();
2997e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        previousItems.addAll(adapter.mItems);
2998e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
2999e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final AtomicInteger layoutStart = new AtomicInteger(50);
3000e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final AtomicBoolean validate = new AtomicBoolean(false);
3001e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final int childCount = 10;
3002e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
3003e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
3004e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3005e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                try {
3006e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    super.onLayoutChildren(recycler, state);
3007e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    if (validate.get()) {
3008e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        assertEquals("Cached views should be kept", 5, recycler
3009e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                .mCachedViews.size());
3010e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        for (RecyclerView.ViewHolder vh : recycler.mCachedViews) {
3011e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            TestViewHolder tvh = (TestViewHolder) vh;
30120bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                            assertTrue("view holder should be marked for update",
30130bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                                    tvh.needsUpdate());
3014e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            assertTrue("view holder should be marked as invalid", tvh.isInvalid());
3015e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        }
3016e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
3017e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    detachAndScrapAttachedViews(recycler);
3018e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    if (validate.get()) {
3019e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        assertEquals("cache size should stay the same", 5,
3020e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                recycler.mCachedViews.size());
3021e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        assertEquals("all views should be scrapped", childCount,
3022e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                recycler.getScrapList().size());
3023e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        for (RecyclerView.ViewHolder vh : recycler.getScrapList()) {
3024e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            // TODO create test case for type change
3025e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            TestViewHolder tvh = (TestViewHolder) vh;
30260bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                            assertTrue("view holder should be marked for update",
30270bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                                    tvh.needsUpdate());
3028e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            assertTrue("view holder should be marked as invalid", tvh.isInvalid());
3029e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        }
3030e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
3031e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    layoutRange(recycler, layoutStart.get(), layoutStart.get() + childCount);
3032e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    if (validate.get()) {
3033e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        for (int i = 0; i < getChildCount(); i++) {
3034e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            View view = getChildAt(i);
3035e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            TestViewHolder tvh = (TestViewHolder) mRecyclerView
3036e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                    .getChildViewHolder(view);
3037115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            final int oldPos = previousItems.indexOf(tvh.mBoundItem);
3038e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            assertEquals("view holder's position should be correct",
3039e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                    oldPositionToNewPositionMapping.get(oldPos).intValue(),
3040115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    tvh.getLayoutPosition());
30410bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                            ;
30420bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        }
3043e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
3044e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                } catch (Throwable t) {
3045e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    postExceptionToInstrumentation(t);
3046e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                } finally {
3047e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    layoutLatch.countDown();
3048e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                }
3049e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
3050e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        };
3051e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
3052e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setItemAnimator(null);
3053e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setAdapter(adapter);
3054e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setLayoutManager(lm);
3055e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setItemViewCacheSize(10);
3056e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.expectLayouts(1);
3057e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        setRecyclerView(recyclerView);
3058e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.waitForLayout(2);
3059e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        checkForMainThreadException();
3060e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        getInstrumentation().waitForIdleSync();
3061e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        layoutStart.set(layoutStart.get() + 5);//55
3062e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.expectLayouts(1);
3063e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        requestLayoutOnUIThread(recyclerView);
3064e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.waitForLayout(2);
3065e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        validate.set(true);
3066e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.expectLayouts(1);
3067e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        runTestOnUiThread(new Runnable() {
3068e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
3069e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public void run() {
3070e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                try {
3071e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    adapter.moveItems(false,
3072e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            new int[]{50, 56}, new int[]{51, 1}, new int[]{52, 2},
3073e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            new int[]{53, 54}, new int[]{60, 61}, new int[]{62, 64},
3074e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            new int[]{75, 58});
3075e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    for (int i = 0; i < previousItems.size(); i++) {
3076e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        Item item = previousItems.get(i);
3077e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        oldPositionToNewPositionMapping.put(i, adapter.mItems.indexOf(item));
3078e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
3079cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    adapter.dispatchDataSetChanged();
3080e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                } catch (Throwable throwable) {
3081e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    postExceptionToInstrumentation(throwable);
3082e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                }
3083e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
3084e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        });
3085e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.waitForLayout(2);
3086e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        checkForMainThreadException();
3087e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
3088e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
3089be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
30900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void callbacksDuringAdapterSwap() throws Throwable {
3091824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        callbacksDuringAdapterChange(true);
3092824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
3093824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3094be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
30950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void callbacksDuringAdapterSet() throws Throwable {
3096824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        callbacksDuringAdapterChange(false);
3097824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
3098824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3099824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    public void callbacksDuringAdapterChange(boolean swap) throws Throwable {
3100824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        final TestAdapter2 adapter1 = swap ? createBinderCheckingAdapter()
3101824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                : createOwnerCheckingAdapter();
3102824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        final TestAdapter2 adapter2 = swap ? createBinderCheckingAdapter()
3103824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                : createOwnerCheckingAdapter();
3104824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3105824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
3106824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
3107824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3108824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                try {
3109824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
3110824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                } catch (Throwable t) {
3111824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    postExceptionToInstrumentation(t);
3112824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                }
3113824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                layoutLatch.countDown();
3114824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
3115824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        };
3116824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
3117824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        rv.setAdapter(adapter1);
3118824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        rv.setLayoutManager(tlm);
3119824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.expectLayouts(1);
3120824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        setRecyclerView(rv);
3121824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.waitForLayout(1);
3122824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        checkForMainThreadException();
3123824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.expectLayouts(1);
3124824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        if (swap) {
3125824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            swapAdapter(adapter2, true);
3126824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        } else {
3127824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            setAdapter(adapter2);
3128824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        }
3129824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        checkForMainThreadException();
3130824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.waitForLayout(1);
3131824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        checkForMainThreadException();
3132824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
3133824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3134824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    private TestAdapter2 createOwnerCheckingAdapter() {
3135824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        return new TestAdapter2(10) {
3136824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
3137824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            public void onViewRecycled(TestViewHolder2 holder) {
3138824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                assertSame("on recycled should be called w/ the creator adapter", this,
3139824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                        holder.mData);
3140824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onViewRecycled(holder);
3141824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
3142824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3143824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
3144824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            public void onBindViewHolder(TestViewHolder2 holder, int position) {
3145824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onBindViewHolder(holder, position);
3146824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                assertSame("on bind should be called w/ the creator adapter", this, holder.mData);
3147824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
3148824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3149824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
3150824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            public TestViewHolder2 onCreateViewHolder(ViewGroup parent,
3151824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    int viewType) {
3152824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                final TestViewHolder2 vh = super.onCreateViewHolder(parent, viewType);
3153824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                vh.mData = this;
3154824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                return vh;
3155824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
3156824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        };
3157824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
3158824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3159824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    private TestAdapter2 createBinderCheckingAdapter() {
3160824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        return new TestAdapter2(10) {
3161824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
3162824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            public void onViewRecycled(TestViewHolder2 holder) {
3163824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                assertSame("on recycled should be called w/ the creator adapter", this,
3164824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                        holder.mData);
3165824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                holder.mData = null;
3166824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onViewRecycled(holder);
3167824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
3168824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3169824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
3170824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            public void onBindViewHolder(TestViewHolder2 holder, int position) {
3171824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onBindViewHolder(holder, position);
3172824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                holder.mData = this;
3173824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
3174824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        };
3175824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
3176824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
3177be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
31780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void findViewById() throws Throwable {
31795ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        findViewByIdTest(false);
31805ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        removeRecyclerView();
31815ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        findViewByIdTest(true);
31825ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar    }
31835ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
31845ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar    public void findViewByIdTest(final boolean supportPredictive) throws Throwable {
31855ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
31865ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final int initialAdapterSize = 20;
31875ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final TestAdapter adapter = new TestAdapter(initialAdapterSize);
31885ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final int deleteStart = 6;
31895ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final int deleteCount = 5;
31905ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        recyclerView.setAdapter(adapter);
31915ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final AtomicBoolean assertPositions = new AtomicBoolean(false);
31925ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
31935ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            @Override
31945ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
31955ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                super.onLayoutChildren(recycler, state);
31965ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                if (assertPositions.get()) {
31975ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                    if (state.isPreLayout()) {
31980bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        for (int i = 0; i < deleteStart; i++) {
31995ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i);
32005ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertNotNull("find view by position for existing items should work "
32015ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    + "fine", view);
32025ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertFalse("view should not be marked as removed",
32035ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
32045ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
32055ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
32060bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        for (int i = 0; i < deleteCount; i++) {
32075ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i + deleteStart);
32085ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertNotNull("find view by position should work fine for removed "
32095ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    + "views in pre-layout", view);
32105ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertTrue("view should be marked as removed",
32115ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
32125ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
32135ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
32145ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        for (int i = deleteStart + deleteCount; i < 20; i++) {
32155ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i);
32165ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertNotNull(view);
32175ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertFalse("view should not be marked as removed",
32185ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
32195ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
32205ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
32215ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                    } else {
32220bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        for (int i = 0; i < initialAdapterSize - deleteCount; i++) {
32235ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i);
3224fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar                            assertNotNull("find view by position for existing item " + i +
3225fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar                                    " should work fine. child count:" + getChildCount(), view);
32265ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            TestViewHolder viewHolder =
32275ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    (TestViewHolder) mRecyclerView.getChildViewHolder(view);
32285ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertSame("should be the correct item " + viewHolder
3229115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    , viewHolder.mBoundItem,
32305ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    adapter.mItems.get(viewHolder.mPosition));
32315ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertFalse("view should not be marked as removed",
32325ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
32335ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
32345ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
32355ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                    }
32365ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                }
32375ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                detachAndScrapAttachedViews(recycler);
32385ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                layoutRange(recycler, state.getItemCount() - 1, -1);
32395ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                layoutLatch.countDown();
32405ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            }
32415ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
32425ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            @Override
32435ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
32445ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                return supportPredictive;
32455ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            }
32465ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        };
32475ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        recyclerView.setLayoutManager(lm);
32485ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.expectLayouts(1);
32495ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        setRecyclerView(recyclerView);
32505ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.waitForLayout(2);
32515ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        getInstrumentation().waitForIdleSync();
32525ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
32535ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        assertPositions.set(true);
32545ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.expectLayouts(supportPredictive ? 2 : 1);
32555ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        adapter.deleteAndNotify(new int[]{deleteStart, deleteCount - 1}, new int[]{deleteStart, 1});
32565ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.waitForLayout(2);
32575ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar    }
32585ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
3259be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
32600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void typeForCache() throws Throwable {
326122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final AtomicInteger viewType = new AtomicInteger(1);
326222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final TestAdapter adapter = new TestAdapter(100) {
326322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
326422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public int getItemViewType(int position) {
326522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                return viewType.get();
326622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
326722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
326822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
326922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public long getItemId(int position) {
327022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                return mItems.get(position).mId;
327122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
327222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        };
327322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        adapter.setHasStableIds(true);
327422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final AtomicInteger layoutStart = new AtomicInteger(2);
327522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final int childCount = 10;
327622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
327722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
327822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
327922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                super.onLayoutChildren(recycler, state);
328022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                detachAndScrapAttachedViews(recycler);
328122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                layoutRange(recycler, layoutStart.get(), layoutStart.get() + childCount);
328222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                layoutLatch.countDown();
328322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
328422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        };
328522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
3286668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        recyclerView.setItemAnimator(null);
328722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        recyclerView.setAdapter(adapter);
328822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        recyclerView.setLayoutManager(lm);
328922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        recyclerView.setItemViewCacheSize(10);
329022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.expectLayouts(1);
329122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        setRecyclerView(recyclerView);
329222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.waitForLayout(2);
329322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        getInstrumentation().waitForIdleSync();
329422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        layoutStart.set(4); // trigger a cache for 3,4
329522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.expectLayouts(1);
329622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        requestLayoutOnUIThread(recyclerView);
329722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.waitForLayout(2);
329822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        //
329922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        viewType.incrementAndGet();
330022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        layoutStart.set(2); // go back to bring views from cache
330122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.expectLayouts(1);
330222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        adapter.mItems.remove(1);
3303cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.dispatchDataSetChanged();
330422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.waitForLayout(2);
330522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        runTestOnUiThread(new Runnable() {
330622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
330722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public void run() {
330822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                for (int i = 2; i < 4; i++) {
3309115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    RecyclerView.ViewHolder vh = recyclerView.findViewHolderForLayoutPosition(i);
331022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                    assertEquals("View holder's type should match latest type", viewType.get(),
331122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                            vh.getItemViewType());
331222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                }
331322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
331422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        });
331522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar    }
331622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
3317be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
33180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void typeForExistingViews() throws Throwable {
33197e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final AtomicInteger viewType = new AtomicInteger(1);
33207e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final int invalidatedCount = 2;
33217e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final int layoutStart = 2;
33227e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final TestAdapter adapter = new TestAdapter(100) {
33237e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
33247e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            public int getItemViewType(int position) {
33257e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                return viewType.get();
33267e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
33277e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
33287e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
33297e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
33307e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    int position) {
33317e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                super.onBindViewHolder(holder, position);
33327e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                if (position >= layoutStart && position < invalidatedCount + layoutStart) {
33337e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    try {
33347e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                        assertEquals("holder type should match current view type at position " +
33357e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                                position, viewType.get(), holder.getItemViewType());
33367e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    } catch (Throwable t) {
33377e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                        postExceptionToInstrumentation(t);
33387e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    }
33397e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                }
33407e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
33417e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
33427e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
33437e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            public long getItemId(int position) {
33447e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                return mItems.get(position).mId;
33457e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
33467e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        };
33477e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        adapter.setHasStableIds(true);
33487e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
33497e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final int childCount = 10;
33507e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
33517e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
33527e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
33537e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                super.onLayoutChildren(recycler, state);
33547e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                detachAndScrapAttachedViews(recycler);
33557e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                layoutRange(recycler, layoutStart, layoutStart + childCount);
33567e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                layoutLatch.countDown();
33577e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
33587e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        };
33597e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
33607e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        recyclerView.setAdapter(adapter);
33617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        recyclerView.setLayoutManager(lm);
33627e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.expectLayouts(1);
33637e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        setRecyclerView(recyclerView);
33647e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.waitForLayout(2);
33657e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        getInstrumentation().waitForIdleSync();
33667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        viewType.incrementAndGet();
33677e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.expectLayouts(1);
3368cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.changeAndNotify(layoutStart, invalidatedCount);
33697e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.waitForLayout(2);
33707e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        checkForMainThreadException();
33717e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar    }
33727e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
33737e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
3374be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
33750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void state() throws Throwable {
3376d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final TestAdapter adapter = new TestAdapter(10);
3377d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final RecyclerView recyclerView = new RecyclerView(getActivity());
3378d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        recyclerView.setAdapter(adapter);
3379d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        recyclerView.setItemAnimator(null);
3380d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final AtomicInteger itemCount = new AtomicInteger();
3381d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final AtomicBoolean structureChanged = new AtomicBoolean();
3382d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        TestLayoutManager testLayoutManager = new TestLayoutManager() {
3383d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
3384d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
33858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                detachAndScrapAttachedViews(recycler);
3386d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                layoutRange(recycler, 0, state.getItemCount());
3387d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                itemCount.set(state.getItemCount());
3388d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.set(state.didStructureChange());
3389d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                layoutLatch.countDown();
3390d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
3391d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        };
3392d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        recyclerView.setLayoutManager(testLayoutManager);
3393d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        testLayoutManager.expectLayouts(1);
3394d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        runTestOnUiThread(new Runnable() {
3395d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
3396d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            public void run() {
3397a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().addView(recyclerView);
3398d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
3399d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        });
340066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        testLayoutManager.waitForLayout(2);
3401d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
3402d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("item count in state should be correct", adapter.getItemCount()
3403d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                , itemCount.get());
3404d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("structure changed should be true for first layout", true,
3405d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
3406d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        Thread.sleep(1000); //wait for other layouts.
3407d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        testLayoutManager.expectLayouts(1);
3408d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        runTestOnUiThread(new Runnable() {
3409d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
3410d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            public void run() {
3411d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                recyclerView.requestLayout();
3412d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
3413d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        });
34148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.waitForLayout(2);
3415d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("in second layout,structure changed should be false", false,
3416d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
34178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.expectLayouts(1); //
3418b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        adapter.deleteAndNotify(3, 2);
34198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.waitForLayout(2);
3420d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("when items are removed, item count in state should be updated",
3421d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                adapter.getItemCount(),
3422d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                itemCount.get());
3423d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("structure changed should be true when items are removed", true,
3424d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
34258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.expectLayouts(1);
3426b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        adapter.addAndNotify(2, 5);
34278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.waitForLayout(2);
3428d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
3429d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("when items are added, item count in state should be updated",
3430d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                adapter.getItemCount(),
3431d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                itemCount.get());
3432d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("structure changed should be true when items are removed", true,
3433d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
34340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
34350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
3436be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
34370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void detachWithoutLayoutManager() throws Throwable {
3438abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
3439abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        runTestOnUiThread(new Runnable() {
3440abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar            @Override
3441abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar            public void run() {
3442abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                try {
3443abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    setRecyclerView(recyclerView);
3444abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    removeRecyclerView();
3445abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                } catch (Throwable t) {
3446abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    postExceptionToInstrumentation(t);
3447abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                }
3448abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar            }
3449abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        });
3450abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        checkForMainThreadException();
3451abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
3452abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
3453be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
34540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void updateHiddenView() throws Throwable {
34551a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
34561a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final int[] preLayoutRange = new int[]{0, 10};
34571a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final int[] postLayoutRange = new int[]{0, 10};
34581a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final AtomicBoolean enableGetViewTest = new AtomicBoolean(false);
3459959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        final List<Integer> disappearingPositions = new ArrayList<>();
34601a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final TestLayoutManager tlm = new TestLayoutManager() {
34611a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            @Override
34621a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
34631a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                return true;
34641a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            }
34651a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
34661a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            @Override
34671a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
34681a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                try {
34691a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    final int[] layoutRange = state.isPreLayout() ? preLayoutRange
34701a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            : postLayoutRange;
34711a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    detachAndScrapAttachedViews(recycler);
34721a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    layoutRange(recycler, layoutRange[0], layoutRange[1]);
34731a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    if (!state.isPreLayout()) {
34741a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                        for (Integer position : disappearingPositions) {
34751a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            // test sanity.
34761a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            assertNull(findViewByPosition(position));
34771a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            final View view = recycler.getViewForPosition(position);
3478959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                            assertNotNull(view);
34791a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            addDisappearingView(view);
34801a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            measureChildWithMargins(view, 0, 0);
34811a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            // position item out of bounds.
34821a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            view.layout(0, -500, view.getMeasuredWidth(),
34831a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                                    -500 + view.getMeasuredHeight());
34841a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                        }
34851a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    }
34861a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                } catch (Throwable t) {
34871a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    postExceptionToInstrumentation(t);
34881a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                }
34891a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                layoutLatch.countDown();
34901a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            }
34911a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        };
3492959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        recyclerView.getItemAnimator().setMoveDuration(4000);
3493959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        recyclerView.getItemAnimator().setRemoveDuration(4000);
34941a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final TestAdapter adapter = new TestAdapter(100);
34951a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        recyclerView.setAdapter(adapter);
34961a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        recyclerView.setLayoutManager(tlm);
34971a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.expectLayouts(1);
34981a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        setRecyclerView(recyclerView);
34991a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.waitForLayout(1);
35001a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        checkForMainThreadException();
35011a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        // now, a child disappears
35021a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        disappearingPositions.add(0);
35031a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        // layout one shifted
35041a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        postLayoutRange[0] = 1;
35051a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        postLayoutRange[1] = 11;
35061a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.expectLayouts(2);
35071a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        adapter.addAndNotify(8, 1);
35081a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.waitForLayout(2);
35091a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        checkForMainThreadException();
35101a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
35111a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.expectLayouts(2);
35121a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        disappearingPositions.clear();
35131a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        // now that item should be moving, invalidate it and delete it.
35141a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        enableGetViewTest.set(true);
35151a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        runTestOnUiThread(new Runnable() {
35161a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            @Override
35171a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            public void run() {
35181a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                try {
3519959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    assertThat("test sanity, should still be animating",
3520959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                            mRecyclerView.isAnimating(), CoreMatchers.is(true));
35211a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    adapter.changeAndNotify(0, 1);
35221a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    adapter.deleteAndNotify(0, 1);
35231a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                } catch (Throwable throwable) {
3524959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    fail(throwable.getMessage());
35251a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                }
35261a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            }
35271a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        });
35281a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.waitForLayout(2);
35291a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        checkForMainThreadException();
35301a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar    }
35311a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
3532be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
35330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnTop() throws Throwable {
35346d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.TOP);
35356d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
35366d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
3537be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
35380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnLeft() throws Throwable {
35396d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.LEFT);
35406d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
35416d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
3542be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
35430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnRight() throws Throwable {
35446d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.RIGHT);
35456d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
35466d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
3547be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
35480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnBottom() throws Throwable {
35496d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.BOTTOM);
35506d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
35516d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
3552be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
35530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnLeftRTL() throws Throwable {
35546d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.LEFT, true);
35556d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals("test sanity", ViewCompat.LAYOUT_DIRECTION_RTL,
35566d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                mRecyclerView.getLayoutManager().getLayoutDirection());
35576d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
35586d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
3559be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
35600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnRightRTL() throws Throwable {
35616d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.RIGHT, true);
35626d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals("test sanity", ViewCompat.LAYOUT_DIRECTION_RTL,
35636d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                mRecyclerView.getLayoutManager().getLayoutDirection());
35646d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
35656d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
35666d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    public void focusTooBigViewTest(final int gravity) throws Throwable {
35676d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(gravity, false);
35686d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
3569be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar
35706d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    public void focusTooBigViewTest(final int gravity, final boolean rtl) throws Throwable {
35716d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
35726d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        if (rtl) {
35736d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            ViewCompat.setLayoutDirection(rv, ViewCompat.LAYOUT_DIRECTION_RTL);
35746d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        }
35756d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger vScrollDist = new AtomicInteger(0);
35766d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger hScrollDist = new AtomicInteger(0);
35776d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger vDesiredDist = new AtomicInteger(0);
35786d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger hDesiredDist = new AtomicInteger(0);
35796d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
35806d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
35816d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
35826d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public int getLayoutDirection() {
35836d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return rtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR;
35846d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
35856d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
35866d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
35876d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
35886d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                detachAndScrapAttachedViews(recycler);
35896d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                final View view = recycler.getViewForPosition(0);
35906d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                addView(view);
35916d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                int left = 0, top = 0;
35926d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                view.setBackgroundColor(Color.rgb(0, 0, 255));
35936d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                switch (gravity) {
35946d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.LEFT:
35956d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.RIGHT:
35966d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        view.measure(
35976d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getWidth() * 1.5),
35986d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.EXACTLY),
35996d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getHeight() * .9),
36006d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.AT_MOST));
36016d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        left = gravity == Gravity.LEFT ? getWidth() - view.getMeasuredWidth() - 80
36026d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                : 90;
36036d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        top = 0;
36046d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        if (ViewCompat.LAYOUT_DIRECTION_RTL == getLayoutDirection()) {
36056d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                            hDesiredDist.set((left + view.getMeasuredWidth()) - getWidth());
36066d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        } else {
36076d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                            hDesiredDist.set(left);
36086d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        }
36096d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        break;
36106d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.TOP:
36116d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.BOTTOM:
36126d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        view.measure(
36136d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getWidth() * .9),
36146d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.AT_MOST),
36156d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getHeight() * 1.5),
36166d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.EXACTLY));
36176d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        top = gravity == Gravity.TOP ? getHeight() - view.getMeasuredHeight() -
36186d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                80 : 90;
36196d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        left = 0;
36206d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        vDesiredDist.set(top);
36216d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        break;
36226d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                }
36236d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
36246d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                view.layout(left, top, left + view.getMeasuredWidth(),
36256d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        top + view.getMeasuredHeight());
36266d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                layoutLatch.countDown();
36276d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
36286d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
36296d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
36306d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public boolean canScrollVertically() {
36316d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return true;
36326d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
36336d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
36346d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
36356d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public boolean canScrollHorizontally() {
36366d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return super.canScrollHorizontally();
36376d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
36386d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
36396d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
36406d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
36416d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    RecyclerView.State state) {
36426d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                vScrollDist.addAndGet(dy);
36436d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                getChildAt(0).offsetTopAndBottom(-dy);
36446d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return dy;
36456d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
36466d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
36476d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
36486d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
36496d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    RecyclerView.State state) {
36506d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                hScrollDist.addAndGet(dx);
36516d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                getChildAt(0).offsetLeftAndRight(-dx);
36526d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return dx;
36536d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
36546d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        };
36556d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
36566d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        rv.setAdapter(adapter);
36576d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        rv.setLayoutManager(tlm);
36586d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        tlm.expectLayouts(1);
36596d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        setRecyclerView(rv);
36606d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        tlm.waitForLayout(2);
36616d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        View view = rv.getChildAt(0);
3662959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", requestFocus(view, true));
3663959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", view.hasFocus());
36646d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals(vDesiredDist.get(), vScrollDist.get());
36656d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals(hDesiredDist.get(), hScrollDist.get());
36666d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals(mRecyclerView.getPaddingTop(), view.getTop());
36676d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        if (rtl) {
3668be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar            assertEquals(mRecyclerView.getWidth() - mRecyclerView.getPaddingRight(),
3669be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar                    view.getRight());
36706d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        } else {
36716d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            assertEquals(mRecyclerView.getPaddingLeft(), view.getLeft());
36726d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        }
36736d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
36746d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
3675be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
36760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void firstLayoutWithAdapterChanges() throws Throwable {
36774bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        final TestAdapter adapter = new TestAdapter(0);
36784bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
36794bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        setVisibility(rv, View.GONE);
36804bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
36814bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            @Override
36824bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
36834bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                try {
36844bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    super.onLayoutChildren(recycler, state);
36854bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
36864bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                } catch (Throwable t) {
36874bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    postExceptionToInstrumentation(t);
36884bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                } finally {
36894bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    layoutLatch.countDown();
36904bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                }
36914bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            }
36924bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar
36934bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            @Override
36944bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            public boolean supportsPredictiveItemAnimations() {
36954bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                return true;
36964bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            }
36974bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        };
36984bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        rv.setLayoutManager(tlm);
36994bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        rv.setAdapter(adapter);
37004bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        rv.setHasFixedSize(true);
37014bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        setRecyclerView(rv);
37024bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        tlm.expectLayouts(1);
37034bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        tlm.assertNoLayout("test sanity, layout should not run", 1);
37044bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        getInstrumentation().waitForIdleSync();
37054bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        runTestOnUiThread(new Runnable() {
37064bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            @Override
37074bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            public void run() {
37084bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                try {
37094bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    adapter.addAndNotify(2);
37104bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                } catch (Throwable throwable) {
37114bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    throwable.printStackTrace();
37124bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                }
37134bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                rv.setVisibility(View.VISIBLE);
37144bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            }
37154bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        });
37164bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        checkForMainThreadException();
37174bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        tlm.waitForLayout(2);
37184bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        assertEquals(2, rv.getChildCount());
37194bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        checkForMainThreadException();
37204bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar    }
37214bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar
37224bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar    @Test
372314d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    public void computeScrollOfsetWithoutLayoutManager() throws Throwable {
372414d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
372514d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        rv.setAdapter(new TestAdapter(10));
372614d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        setRecyclerView(rv);
372714d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollExtent());
372814d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollOffset());
372914d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollRange());
373014d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
373114d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollExtent());
373214d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollOffset());
373314d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollRange());
373414d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    }
373514d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
373614d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    @Test
373714d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    public void computeScrollOfsetWithoutAdapter() throws Throwable {
373814d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
373914d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        rv.setLayoutManager(new TestLayoutManager());
374014d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        setRecyclerView(rv);
374114d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollExtent());
374214d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollOffset());
374314d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollRange());
374414d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
374514d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollExtent());
374614d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollOffset());
374714d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollRange());
374814d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    }
374914d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
375014d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    @Test
37510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusRectOnScreenWithDecorOffsets() throws Throwable {
3752f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        focusRectOnScreenTest(true);
3753f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    }
3754f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
3755be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
37560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusRectOnScreenWithout() throws Throwable {
3757f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        focusRectOnScreenTest(false);
3758f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    }
3759f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
3760f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    public void focusRectOnScreenTest(boolean addItemDecors) throws Throwable {
3761f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
3762f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        final AtomicInteger scrollDist = new AtomicInteger(0);
3763f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
3764f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            @Override
3765f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3766f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                detachAndScrapAttachedViews(recycler);
3767f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                final View view = recycler.getViewForPosition(0);
3768f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                addView(view);
3769f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                measureChildWithMargins(view, 0, 0);
3770f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                view.layout(0, -20, view.getWidth(),
3771f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                        -20 + view.getHeight());// ignore decors on purpose
3772f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                layoutLatch.countDown();
3773f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            }
3774f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
3775f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            @Override
3776f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            public boolean canScrollVertically() {
3777f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                return true;
3778f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            }
3779f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
3780f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            @Override
3781f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
3782f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                    RecyclerView.State state) {
3783f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                scrollDist.addAndGet(dy);
3784f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                return dy;
3785f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            }
3786f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        };
3787f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
3788f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        if (addItemDecors) {
3789f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            rv.addItemDecoration(new RecyclerView.ItemDecoration() {
3790f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                @Override
3791f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
3792f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                        RecyclerView.State state) {
3793f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                    outRect.set(0, 10, 0, 10);
3794f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                }
3795f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            });
3796f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        }
3797f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        rv.setAdapter(adapter);
3798f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        rv.setLayoutManager(tlm);
3799f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        tlm.expectLayouts(1);
3800f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        setRecyclerView(rv);
3801f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        tlm.waitForLayout(2);
3802f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
3803f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        View view = rv.getChildAt(0);
3804959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        requestFocus(view, true);
3805f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        assertEquals(addItemDecors ? -30 : -20, scrollDist.get());
3806f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    }
3807f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
3808e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    @Test
38090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void unimplementedSmoothScroll() throws Throwable {
3810e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final AtomicInteger receivedScrollToPosition = new AtomicInteger(-1);
3811e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final AtomicInteger receivedSmoothScrollToPosition = new AtomicInteger(-1);
3812e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final CountDownLatch cbLatch = new CountDownLatch(2);
3813e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
3814e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
3815e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3816e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                detachAndScrapAttachedViews(recycler);
3817e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                layoutRange(recycler, 0, 10);
3818e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                layoutLatch.countDown();
3819e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
3820e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3821e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
3822e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
3823e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                    int position) {
3824e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                assertEquals(-1, receivedSmoothScrollToPosition.get());
3825e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                receivedSmoothScrollToPosition.set(position);
3826e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                RecyclerView.SmoothScroller ss =
3827e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        new LinearSmoothScroller(recyclerView.getContext()) {
3828e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
3829e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            public PointF computeScrollVectorForPosition(int targetPosition) {
3830e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                return null;
3831e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
3832e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        };
3833e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                ss.setTargetPosition(position);
3834e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                startSmoothScroll(ss);
3835e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                cbLatch.countDown();
3836e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
3837e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3838e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
3839e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void scrollToPosition(int position) {
3840e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                assertEquals(-1, receivedScrollToPosition.get());
3841e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                receivedScrollToPosition.set(position);
3842e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                cbLatch.countDown();
3843e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
3844e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        };
3845e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
3846e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setAdapter(new TestAdapter(100));
3847e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setLayoutManager(tlm);
3848e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.expectLayouts(1);
3849e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        setRecyclerView(rv);
3850e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.waitForLayout(2);
3851ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
3852959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        smoothScrollToPosition(35, false);
3853ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("smoothScrollToPosition should be ignored when frozen",
3854ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                -1, receivedSmoothScrollToPosition.get());
3855ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
3856959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        smoothScrollToPosition(35, false);
3857e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertTrue("both scrolls should be called", cbLatch.await(3, TimeUnit.SECONDS));
3858e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        checkForMainThreadException();
3859e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertEquals(35, receivedSmoothScrollToPosition.get());
3860e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertEquals(35, receivedScrollToPosition.get());
3861e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
3862e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3863e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    @Test
38640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void jumpingJackSmoothScroller() throws Throwable {
3865e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        jumpingJackSmoothScrollerTest(true);
3866e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
3867e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3868e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    @Test
38690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void jumpingJackSmoothScrollerGoesIdle() throws Throwable {
3870e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        jumpingJackSmoothScrollerTest(false);
3871e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
3872e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
38737f5e94985f795f81884334ca2752a0469a998762Yigit Boyar    @Test
38747f5e94985f795f81884334ca2752a0469a998762Yigit Boyar    public void testScrollByBeforeFirstLayout() throws Throwable {
38757f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
38767f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
38777f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        recyclerView.setLayoutManager(new TestLayoutManager() {
38787f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            AtomicBoolean didLayout = new AtomicBoolean(false);
38797f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
38807f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
38817f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                super.onLayoutChildren(recycler, state);
38827f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                didLayout.set(true);
38837f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
38847f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
38857f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
38867f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
38877f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    RecyclerView.State state) {
38887f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                assertThat("should run layout before scroll",
38897f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                        didLayout.get(), CoreMatchers.is(true));
38907f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                return super.scrollVerticallyBy(dy, recycler, state);
38917f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
38927f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
38937f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
38947f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public boolean canScrollVertically() {
38957f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                return true;
38967f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
38977f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        });
38987f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        recyclerView.setAdapter(adapter);
38997f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
39007f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        runTestOnUiThread(new Runnable() {
39017f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
39027f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public void run() {
39037f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                try {
39047f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    setRecyclerView(recyclerView);
39057f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    recyclerView.scrollBy(10, 19);
39067f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                } catch (Throwable throwable) {
39077f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    postExceptionToInstrumentation(throwable);
39087f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                }
39097f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
39107f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        });
39117f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
39127f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        checkForMainThreadException();
39137f5e94985f795f81884334ca2752a0469a998762Yigit Boyar    }
39147f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
3915e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    private void jumpingJackSmoothScrollerTest(final boolean succeed) throws Throwable {
3916e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final List<Integer> receivedScrollToPositions = new ArrayList<>();
3917e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final TestAdapter testAdapter = new TestAdapter(200);
3918e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final AtomicBoolean mTargetFound = new AtomicBoolean(false);
3919e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
3920e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            int pendingScrollPosition = -1;
3921e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
3922e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3923e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                detachAndScrapAttachedViews(recycler);
3924e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                final int pos = pendingScrollPosition < 0 ? 0: pendingScrollPosition;
3925e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                layoutRange(recycler, pos, pos + 10);
3926e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                if (layoutLatch != null) {
3927e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                    layoutLatch.countDown();
3928e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                }
3929e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
3930e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3931e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
3932e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
3933e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                    final int position) {
3934e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                RecyclerView.SmoothScroller ss =
3935e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        new LinearSmoothScroller(recyclerView.getContext()) {
3936e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
3937e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            public PointF computeScrollVectorForPosition(int targetPosition) {
3938e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                return new PointF(0, 1);
3939e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
3940e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3941e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
3942e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            protected void onTargetFound(View targetView, RecyclerView.State state,
3943e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                                         Action action) {
3944e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                super.onTargetFound(targetView, state, action);
3945e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                mTargetFound.set(true);
3946e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
3947e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3948e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
3949e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            protected void updateActionForInterimTarget(Action action) {
3950e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                int limit = succeed ? getTargetPosition() : 100;
3951e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                if (pendingScrollPosition + 2 < limit) {
3952e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                    if (pendingScrollPosition != NO_POSITION) {
3953e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                        assertEquals(pendingScrollPosition,
3954e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                                getChildViewHolderInt(getChildAt(0))
3955e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                                        .getAdapterPosition());
3956e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                    }
3957e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                    action.jumpTo(pendingScrollPosition + 2);
3958e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                }
3959e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
3960e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        };
3961e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                ss.setTargetPosition(position);
3962e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                startSmoothScroll(ss);
3963e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
3964e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3965e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
3966e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void scrollToPosition(int position) {
3967e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                receivedScrollToPositions.add(position);
3968e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                pendingScrollPosition = position;
3969e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                requestLayout();
3970e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
3971e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        };
3972e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
3973e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setAdapter(testAdapter);
3974e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setLayoutManager(tlm);
3975e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3976e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.expectLayouts(1);
3977e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        setRecyclerView(rv);
3978e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.waitForLayout(2);
3979e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3980e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        runTestOnUiThread(new Runnable() {
3981e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
3982e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void run() {
3983e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                rv.smoothScrollToPosition(150);
3984e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
3985e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        });
3986e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        int limit = 100;
3987e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        while (rv.getLayoutManager().isSmoothScrolling() && --limit > 0) {
3988e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            Thread.sleep(200);
3989e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            checkForMainThreadException();
3990e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        }
3991e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        checkForMainThreadException();
3992e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertTrue(limit > 0);
3993e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        for (int i = 1; i < 100; i+=2) {
3994e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            assertTrue("scroll positions must include " + i, receivedScrollToPositions.contains(i));
3995e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        }
3996e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3997e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertEquals(succeed, mTargetFound.get());
3998e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
3999e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
4000e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
40010b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    private static class TestViewHolder2 extends RecyclerView.ViewHolder {
4002824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4003824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        Object mData;
4004824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
40050b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        public TestViewHolder2(View itemView) {
40060b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            super(itemView);
40070b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
40080b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
4009d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
40100b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    private static class TestAdapter2 extends RecyclerView.Adapter<TestViewHolder2> {
4011824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
40120b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        List<Item> mItems;
40130b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
40140b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        private TestAdapter2(int count) {
40150b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            mItems = new ArrayList<Item>(count);
40160b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            for (int i = 0; i < count; i++) {
40170b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                mItems.add(new Item(i, "Item " + i));
40180b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
40190b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
40200b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
40210b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        @Override
40220b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        public TestViewHolder2 onCreateViewHolder(ViewGroup parent,
40230b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                int viewType) {
40240b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            return new TestViewHolder2(new TextView(parent.getContext()));
40250b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
40260b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
40270b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        @Override
40280b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        public void onBindViewHolder(TestViewHolder2 holder, int position) {
40290b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            final Item item = mItems.get(position);
40300b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")");
40310b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
40320b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
40330b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        @Override
40340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        public int getItemCount() {
40350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            return mItems.size();
40360b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
4037d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
4038d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
4039e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar    public interface AdapterRunnable {
40401a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
40413d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        void run(TestAdapter adapter) throws Throwable;
4042115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
4043115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
40443d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    public class LayoutAllLayoutManager extends TestLayoutManager {
40453d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        @Override
40463d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
40473d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            layoutRange(recycler, 0, state.getItemCount());
40483d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            layoutLatch.countDown();
40493d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        }
40503d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    }
4051bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
4052bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    /**
4053bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar     * Proxy class to make protected methods public
4054bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar     */
4055bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    public static class TestRecyclerView extends RecyclerView {
4056bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
4057bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public TestRecyclerView(Context context) {
4058bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super(context);
4059bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
4060bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
4061bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public TestRecyclerView(Context context, @Nullable AttributeSet attrs) {
4062bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super(context, attrs);
4063bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
4064bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
4065bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public TestRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
4066bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super(context, attrs, defStyle);
4067bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
4068bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
4069bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        @Override
4070bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public void detachViewFromParent(int index) {
4071bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super.detachViewFromParent(index);
4072bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
4073bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
4074bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        @Override
4075bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public void attachViewToParent(View child, int index, ViewGroup.LayoutParams params) {
4076bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super.attachViewToParent(child, index, params);
4077bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
4078bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    }
40794510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
40804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    private static interface ViewRunnable {
40814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        void run(View view) throws RuntimeException;
40824510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
40837a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar}
4084