1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 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
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget;
18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
21ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
22ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_SETTLING;
23ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.RecyclerView.getChildViewHolderInt;
2432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar
257b926c6cb92ecc663439b200559d3c0fcfa86980shepshapardimport static org.hamcrest.CoreMatchers.equalTo;
2632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.hamcrest.CoreMatchers.is;
27178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyarimport static org.hamcrest.CoreMatchers.not;
28993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapardimport static org.hamcrest.CoreMatchers.notNullValue;
2932b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.hamcrest.CoreMatchers.sameInstance;
3032b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertEquals;
3132b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertFalse;
3232b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertNotNull;
3332b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertNotSame;
3432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertNull;
3532b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertSame;
3632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertThat;
3732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.assertTrue;
3832b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.junit.Assert.fail;
3932b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.any;
4032b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.anyInt;
4176daed103193a1756535d1f59b165e98e1d17445Chris Banesimport static org.mockito.Mockito.atLeastOnce;
4232b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.eq;
4332b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.mock;
4432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.never;
4532b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.spy;
4632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.times;
4732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport static org.mockito.Mockito.verify;
48be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar
49bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyarimport android.content.Context;
502e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapardimport android.graphics.Canvas;
516d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyarimport android.graphics.Color;
52ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyarimport android.graphics.PointF;
53f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyarimport android.graphics.Rect;
5432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport android.os.Build;
55bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport android.os.SystemClock;
56b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viveretteimport android.support.test.filters.FlakyTest;
5735232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikasimport android.support.test.filters.LargeTest;
5832b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport android.support.test.filters.SdkSuppress;
59b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viveretteimport android.support.test.filters.Suppress;
604143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.support.test.runner.AndroidJUnit4;
61bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyarimport android.util.AttributeSet;
62ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyarimport android.util.Log;
63670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyarimport android.view.Gravity;
64bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport android.view.MotionEvent;
655ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyarimport android.view.View;
66bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyarimport android.view.ViewConfiguration;
670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyarimport android.view.ViewGroup;
684965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyarimport android.view.ViewTreeObserver;
69a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyarimport android.widget.LinearLayout;
700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyarimport android.widget.TextView;
715ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
72c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.NonNull;
73c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.annotation.Nullable;
74c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.core.view.NestedScrollingParent2;
75c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.core.view.ViewCompat;
76c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikasimport androidx.recyclerview.test.NestedScrollingParent2Adapter;
77c95a6f1f125ad3a7e1f9f79bccf4b2603bc40ebaAurimas Liutikas
7832b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport org.hamcrest.CoreMatchers;
7932b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport org.hamcrest.MatcherAssert;
8032b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport org.junit.Test;
8132b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyarimport org.junit.runner.RunWith;
8232b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar
83e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyarimport java.util.ArrayList;
84e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyarimport java.util.HashMap;
850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyarimport java.util.List;
86e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyarimport java.util.Map;
87ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyarimport java.util.concurrent.CountDownLatch;
88d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit;
89d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.atomic.AtomicBoolean;
90d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.atomic.AtomicInteger;
91824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
92be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar@RunWith(AndroidJUnit4.class)
9335232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas@LargeTest
94d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepublic class RecyclerViewLayoutTest extends BaseRecyclerViewInstrumentationTest {
95cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private static final int FLAG_HORIZONTAL = 1;
96cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private static final int FLAG_VERTICAL = 1 << 1;
97cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private static final int FLAG_FLING = 1 << 2;
98d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
997bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar    private static final boolean DEBUG = false;
10022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
101ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    private static final String TAG = "RecyclerViewLayoutTest";
102ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
10322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar    public RecyclerViewLayoutTest() {
10422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        super(DEBUG);
10522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar    }
106bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
107be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
1084f11964463526a83536838d6afffb915b2c836a8Yigit Boyar    public void triggerFocusSearchInOnRecycledCallback() throws Throwable {
1094f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        final RecyclerView rv = new RecyclerView(getActivity()) {
1104f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            @Override
1114f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            void consumePendingUpdateOperations() {
1124f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                try {
1134f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                    super.consumePendingUpdateOperations();
1144f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                } catch (Throwable t) {
1154f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                    postExceptionToInstrumentation(t);
1164f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                }
1174f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            }
1184f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        };
1194f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        final AtomicBoolean receivedOnRecycled = new AtomicBoolean(false);
1204f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        final TestAdapter adapter = new TestAdapter(20) {
1214f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            @Override
1228a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull TestViewHolder holder) {
1234f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                super.onViewRecycled(holder);
1244f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                if (receivedOnRecycled.getAndSet(true)) {
1254f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                    return;
1264f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                }
1274f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                rv.focusSearch(rv.getChildAt(0), View.FOCUS_FORWARD);
1284f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            }
1294f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        };
1304f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        final AtomicInteger layoutCnt = new AtomicInteger(5);
1314f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
1324f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            @Override
1334f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1344f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                detachAndScrapAttachedViews(recycler);
1354f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                layoutRange(recycler, 0, layoutCnt.get());
1364f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                layoutLatch.countDown();
1374f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            }
1384f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        };
1394f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        rv.setLayoutManager(tlm);
1404f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        rv.setAdapter(adapter);
1414f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        tlm.expectLayouts(1);
1424f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        setRecyclerView(rv);
1434f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        tlm.waitForLayout(2);
1444f11964463526a83536838d6afffb915b2c836a8Yigit Boyar
1454f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        layoutCnt.set(4);
1464f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        tlm.expectLayouts(1);
1474f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        requestLayoutOnUIThread(rv);
1484f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        tlm.waitForLayout(1);
1494f11964463526a83536838d6afffb915b2c836a8Yigit Boyar
1504f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        assertThat("test sanity", rv.mRecycler.mCachedViews.size(), is(1));
1514f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        tlm.expectLayouts(1);
152573e17f638a8303523bdd9625e43f3acc53a0a43Yigit Boyar        mActivityRule.runOnUiThread(new Runnable() {
1534f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            @Override
1544f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            public void run() {
1554f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                adapter.notifyItemChanged(4);
1564f11964463526a83536838d6afffb915b2c836a8Yigit Boyar                rv.smoothScrollBy(0, 1);
1574f11964463526a83536838d6afffb915b2c836a8Yigit Boyar            }
1584f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        });
1594f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        checkForMainThreadException();
1604f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        tlm.waitForLayout(2);
1614f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        assertThat("test sanity", rv.mRecycler.mCachedViews.size(), is(0));
1624f11964463526a83536838d6afffb915b2c836a8Yigit Boyar        assertThat(receivedOnRecycled.get(), is(true));
1634f11964463526a83536838d6afffb915b2c836a8Yigit Boyar    }
1644f11964463526a83536838d6afffb915b2c836a8Yigit Boyar
1654f11964463526a83536838d6afffb915b2c836a8Yigit Boyar    @Test
166a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyWithoutChanges() throws Throwable {
167a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(false, false, false);
168a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
169a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
170a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
171a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyRequireLayout() throws Throwable {
172a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(true, false, false);
173a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
174a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
175a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
176a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyRemoveAdapter() throws Throwable {
177a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(false, true, false);
178a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
179a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
180a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
181a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    public void detachAttachGetReadyRemoveLayoutManager() throws Throwable {
182a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        detachAttachGetReady(false, false, true);
183a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
184a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
185a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    private void detachAttachGetReady(final boolean requestLayoutOnDetach,
186a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            final boolean removeAdapter, final boolean removeLayoutManager) throws Throwable {
187a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final LinearLayout ll1 = new LinearLayout(getActivity());
188a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final LinearLayout ll2 = new LinearLayout(getActivity());
189a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final LinearLayout ll3 = new LinearLayout(getActivity());
190a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
191a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
192a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        ll1.addView(ll2);
193a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        ll2.addView(ll3);
194a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        ll3.addView(rv);
195a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        TestLayoutManager layoutManager = new TestLayoutManager() {
196a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
197a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void onLayoutCompleted(RecyclerView.State state) {
198a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                super.onLayoutCompleted(state);
199a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                layoutLatch.countDown();
200a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
201a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
202a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
203a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
204a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                super.onDetachedFromWindow(view, recycler);
205a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                if (requestLayoutOnDetach) {
206a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    view.requestLayout();
207a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                }
208a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
209a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        };
210a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        rv.setLayoutManager(layoutManager);
211a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        rv.setAdapter(new TestAdapter(10));
212a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        layoutManager.expectLayouts(1);
21342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
214a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
215a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void run() {
216a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().addView(ll1);
217a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
218a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        });
219a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        layoutManager.waitForLayout(2);
22042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
221a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
222a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void run() {
223a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                ll1.removeView(ll2);
224a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
225a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        });
226a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        getInstrumentation().waitForIdleSync();
227a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        if (removeLayoutManager) {
228a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setLayoutManager(null);
229a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setLayoutManager(layoutManager);
230a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        }
231a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        if (removeAdapter) {
232a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setAdapter(null);
233a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            rv.setAdapter(new TestAdapter(10));
234a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        }
235a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        final boolean requireLayout = requestLayoutOnDetach || removeAdapter || removeLayoutManager;
236a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        layoutManager.expectLayouts(1);
23742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
238a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            @Override
239a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            public void run() {
240a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                ll1.addView(ll2);
241a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                if (requireLayout) {
242a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertTrue(rv.hasPendingAdapterUpdates());
243a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertFalse(rv.mFirstLayoutComplete);
244a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                } else {
245a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertFalse(rv.hasPendingAdapterUpdates());
246a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                    assertTrue(rv.mFirstLayoutComplete);
247a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                }
248a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            }
249a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        });
250a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        if (requireLayout) {
251a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            layoutManager.waitForLayout(2);
252a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        } else {
253a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar            layoutManager.assertNoLayout("nothing is invalid, layout should not happen", 2);
254a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar        }
255a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    }
256a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar
257a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar    @Test
258c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void setAdapter_afterSwapAdapter_callsCorrectLmMethods() throws Throwable {
259c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
260c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true);
261c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final TestAdapter testAdapter = new TestAdapter(1);
262c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
263c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
264c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        rv.setLayoutManager(lm);
265c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setRecyclerView(rv);
266c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setAdapter(testAdapter);
267c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
268c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
269c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onAdapterChagnedCallCount = 0;
270c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onItemsChangedCallCount = 0;
271c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
272c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
273c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mActivityRule.runOnUiThread(
274c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                new Runnable() {
275c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    @Override
276c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    public void run() {
277c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.swapAdapter(testAdapter, true);
278c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.setAdapter(testAdapter);
279c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    }
280c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                });
281c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
282c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
283c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(2, lm.onAdapterChagnedCallCount);
284c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onItemsChangedCallCount);
285c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
286c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
287c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
288c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void setAdapter_afterNotifyDataSetChanged_callsCorrectLmMethods() throws Throwable {
289c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
290c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true);
291c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final TestAdapter testAdapter = new TestAdapter(1);
292c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
293c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
294c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        rv.setLayoutManager(lm);
295c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setRecyclerView(rv);
296c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setAdapter(testAdapter);
297c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
298c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
299c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onAdapterChagnedCallCount = 0;
300c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onItemsChangedCallCount = 0;
301c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
302c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
303c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mActivityRule.runOnUiThread(
304c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                new Runnable() {
305c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    @Override
306c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    public void run() {
307c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        testAdapter.notifyDataSetChanged();
308c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.setAdapter(testAdapter);
309c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    }
310c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                });
311c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
312c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
313c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onAdapterChagnedCallCount);
314c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onItemsChangedCallCount);
315c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
316c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
317c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
318c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void notifyDataSetChanged_afterSetAdapter_callsCorrectLmMethods() throws Throwable {
319c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
320c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true);
321c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final TestAdapter testAdapter = new TestAdapter(1);
322c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
323c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
324c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        rv.setLayoutManager(lm);
325c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setRecyclerView(rv);
326c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setAdapter(testAdapter);
327c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
328c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
329c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onAdapterChagnedCallCount = 0;
330c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onItemsChangedCallCount = 0;
331c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
332c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
333c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mActivityRule.runOnUiThread(
334c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                new Runnable() {
335c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    @Override
336c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    public void run() {
337c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.setAdapter(testAdapter);
338c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        testAdapter.notifyDataSetChanged();
339c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    }
340c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                });
341c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
342c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
343c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onAdapterChagnedCallCount);
344c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onItemsChangedCallCount);
345c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
346c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
347c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
348c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void notifyDataSetChanged_afterSwapAdapter_callsCorrectLmMethods() throws Throwable {
349c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
350c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true);
351c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final TestAdapter testAdapter = new TestAdapter(1);
352c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
353c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
354c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        rv.setLayoutManager(lm);
355c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setRecyclerView(rv);
356c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setAdapter(testAdapter);
357c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
358c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
359c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onAdapterChagnedCallCount = 0;
360c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onItemsChangedCallCount = 0;
361c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
362c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
363c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mActivityRule.runOnUiThread(
364c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                new Runnable() {
365c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    @Override
366c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    public void run() {
367c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.swapAdapter(testAdapter, true);
368c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        testAdapter.notifyDataSetChanged();
369c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    }
370c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                });
371c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
372c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
373c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onAdapterChagnedCallCount);
374c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onItemsChangedCallCount);
375c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
376c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
377c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
378c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void swapAdapter_afterSetAdapter_callsCorrectLmMethods() throws Throwable {
379c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
380c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true);
381c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final TestAdapter testAdapter = new TestAdapter(1);
382c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
383c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
384c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        rv.setLayoutManager(lm);
385c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setRecyclerView(rv);
386c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setAdapter(testAdapter);
387c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
388c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
389c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onAdapterChagnedCallCount = 0;
390c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onItemsChangedCallCount = 0;
391c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
392c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
393c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mActivityRule.runOnUiThread(
394c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                new Runnable() {
395c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    @Override
396c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    public void run() {
397c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.setAdapter(testAdapter);
398c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.swapAdapter(testAdapter, true);
399c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    }
400c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                });
401c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
402c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
403c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(2, lm.onAdapterChagnedCallCount);
404c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onItemsChangedCallCount);
405c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
406c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
407c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
408c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    public void swapAdapter_afterNotifyDataSetChanged_callsCorrectLmMethods() throws Throwable {
409c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
410c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true);
411c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        final TestAdapter testAdapter = new TestAdapter(1);
412c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
413c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
414c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        rv.setLayoutManager(lm);
415c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setRecyclerView(rv);
416c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        setAdapter(testAdapter);
417c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
418c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
419c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onAdapterChagnedCallCount = 0;
420c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.onItemsChangedCallCount = 0;
421c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
422c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.expectLayouts(1);
423c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        mActivityRule.runOnUiThread(
424c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                new Runnable() {
425c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    @Override
426c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    public void run() {
427c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        testAdapter.notifyDataSetChanged();
428c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                        rv.swapAdapter(testAdapter, true);
429c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                    }
430c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                });
431c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        lm.waitForLayout(2);
432c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
433c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onAdapterChagnedCallCount);
434c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        assertEquals(1, lm.onItemsChangedCallCount);
435c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    }
436c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
437c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard    @Test
43848560f9d06a8e43c190906d01d9abc55df93e899shepshapard    public void setAdapterNotifyItemRangeInsertedCrashTest() throws Throwable {
43948560f9d06a8e43c190906d01d9abc55df93e899shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
44048560f9d06a8e43c190906d01d9abc55df93e899shepshapard        final TestLayoutManager lm = new LayoutAllLayoutManager(true);
44148560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.setSupportsPredictive(true);
44248560f9d06a8e43c190906d01d9abc55df93e899shepshapard        rv.setLayoutManager(lm);
44348560f9d06a8e43c190906d01d9abc55df93e899shepshapard        setRecyclerView(rv);
44448560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.expectLayouts(1);
44548560f9d06a8e43c190906d01d9abc55df93e899shepshapard        setAdapter(new TestAdapter(1));
44648560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.waitForLayout(2);
44748560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.expectLayouts(1);
44848560f9d06a8e43c190906d01d9abc55df93e899shepshapard        mActivityRule.runOnUiThread(new Runnable() {
44948560f9d06a8e43c190906d01d9abc55df93e899shepshapard            @Override
45048560f9d06a8e43c190906d01d9abc55df93e899shepshapard            public void run() {
45148560f9d06a8e43c190906d01d9abc55df93e899shepshapard                final TestAdapter adapter2 = new TestAdapter(0);
45248560f9d06a8e43c190906d01d9abc55df93e899shepshapard                rv.setAdapter(adapter2);
45348560f9d06a8e43c190906d01d9abc55df93e899shepshapard                adapter2.addItems(0, 1, "1");
45448560f9d06a8e43c190906d01d9abc55df93e899shepshapard                adapter2.notifyItemRangeInserted(0, 1);
45548560f9d06a8e43c190906d01d9abc55df93e899shepshapard            }
45648560f9d06a8e43c190906d01d9abc55df93e899shepshapard        });
45748560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.waitForLayout(2);
45848560f9d06a8e43c190906d01d9abc55df93e899shepshapard    }
45948560f9d06a8e43c190906d01d9abc55df93e899shepshapard
46048560f9d06a8e43c190906d01d9abc55df93e899shepshapard    @Test
46148560f9d06a8e43c190906d01d9abc55df93e899shepshapard    public void swapAdapterNotifyItemRangeInsertedCrashTest() throws Throwable {
46248560f9d06a8e43c190906d01d9abc55df93e899shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
46348560f9d06a8e43c190906d01d9abc55df93e899shepshapard        final TestLayoutManager lm = new LayoutAllLayoutManager(true);
46448560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.setSupportsPredictive(true);
46548560f9d06a8e43c190906d01d9abc55df93e899shepshapard        rv.setLayoutManager(lm);
46648560f9d06a8e43c190906d01d9abc55df93e899shepshapard        setRecyclerView(rv);
46748560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.expectLayouts(1);
46848560f9d06a8e43c190906d01d9abc55df93e899shepshapard        setAdapter(new TestAdapter(1));
46948560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.waitForLayout(2);
47048560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.expectLayouts(1);
47148560f9d06a8e43c190906d01d9abc55df93e899shepshapard        mActivityRule.runOnUiThread(new Runnable() {
47248560f9d06a8e43c190906d01d9abc55df93e899shepshapard            @Override
47348560f9d06a8e43c190906d01d9abc55df93e899shepshapard            public void run() {
47448560f9d06a8e43c190906d01d9abc55df93e899shepshapard                final TestAdapter adapter2 = new TestAdapter(0);
47548560f9d06a8e43c190906d01d9abc55df93e899shepshapard                rv.swapAdapter(adapter2, true);
47648560f9d06a8e43c190906d01d9abc55df93e899shepshapard                adapter2.addItems(0, 1, "1");
47748560f9d06a8e43c190906d01d9abc55df93e899shepshapard                adapter2.notifyItemRangeInserted(0, 1);
47848560f9d06a8e43c190906d01d9abc55df93e899shepshapard            }
47948560f9d06a8e43c190906d01d9abc55df93e899shepshapard        });
48048560f9d06a8e43c190906d01d9abc55df93e899shepshapard        lm.waitForLayout(2);
48148560f9d06a8e43c190906d01d9abc55df93e899shepshapard    }
48248560f9d06a8e43c190906d01d9abc55df93e899shepshapard
48348560f9d06a8e43c190906d01d9abc55df93e899shepshapard    @Test
484993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    public void onDataSetChanged_doesntHaveStableIds_cachedViewHasNoPosition() throws Throwable {
485993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        onDataSetChanged_handleCachedViews(false);
486993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    }
487993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
488993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    @Test
489993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    public void onDataSetChanged_hasStableIds_noCachedViewsAreRecycled() throws Throwable {
490993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        onDataSetChanged_handleCachedViews(true);
491993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    }
492993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
493993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    /**
494993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard     * If Adapter#setHasStableIds(boolean) is false, cached ViewHolders should be recycled in
495993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard     * response to RecyclerView.Adapter#notifyDataSetChanged() and should report a position of
496993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard     * RecyclerView#NO_POSITION inside of
497993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard     * RecyclerView.Adapter#onViewRecycled(RecyclerView.ViewHolder).
498993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard     *
499993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard     * If Adapter#setHasStableIds(boolean) is true, cached Views/ViewHolders should not be recycled.
500993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard     */
501993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    public void onDataSetChanged_handleCachedViews(boolean hasStableIds) throws Throwable {
502993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final AtomicInteger cachedRecycleCount = new AtomicInteger(0);
503993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
504993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final RecyclerView recyclerView = new RecyclerView(getActivity());
505993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        recyclerView.setItemViewCacheSize(1);
506993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
507993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final TestAdapter adapter = new TestAdapter(2) {
508993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
5098a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull TestViewHolder holder) {
510993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                // If the recycled holder is currently in the cache, then it's position in the
511993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                // adapter should be RecyclerView.NO_POSITION.
512993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                if (mRecyclerView.mRecycler.mCachedViews.contains(holder)) {
513993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    assertThat("ViewHolder's getAdapterPosition should be "
514993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                                    + "RecyclerView.NO_POSITION",
515993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                            holder.getAdapterPosition(),
516993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                            is(RecyclerView.NO_POSITION));
517993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    cachedRecycleCount.incrementAndGet();
518993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                }
519993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                super.onViewRecycled(holder);
520993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
521993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        };
522993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        adapter.setHasStableIds(hasStableIds);
523993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        recyclerView.setAdapter(adapter);
524993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
525993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final AtomicInteger numItemsToLayout = new AtomicInteger(2);
526993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
527993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        TestLayoutManager layoutManager = new TestLayoutManager() {
528993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
529993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
530993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                try {
531993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    detachAndScrapAttachedViews(recycler);
532993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    layoutRange(recycler, 0, numItemsToLayout.get());
533993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                } catch (Throwable t) {
534993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    postExceptionToInstrumentation(t);
535993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                } finally {
536993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    this.layoutLatch.countDown();
537993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                }
538993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
539993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
540993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
541993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            public boolean supportsPredictiveItemAnimations() {
542993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                return false;
543993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
544993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        };
545993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        recyclerView.setLayoutManager(layoutManager);
546993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
547993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // Layout 2 items and sanity check that no items are in the recycler's cache.
548993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        numItemsToLayout.set(2);
549993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutManager.expectLayouts(1);
550993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        setRecyclerView(recyclerView, true, false);
551993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutManager.waitForLayout(2);
552993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        checkForMainThreadException();
553993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        assertThat("Sanity check, no views should be cached at this time",
554993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                mRecyclerView.mRecycler.mCachedViews.size(),
555993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                is(0));
556993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
557993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // Now only layout 1 item and assert that 1 item is cached.
558993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        numItemsToLayout.set(1);
559993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutManager.expectLayouts(1);
560993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        requestLayoutOnUIThread(mRecyclerView);
561993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutManager.waitForLayout(1);
562993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        checkForMainThreadException();
563993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        assertThat("One view should be cached.",
564993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                mRecyclerView.mRecycler.mCachedViews.size(),
565993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                is(1));
566993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
567993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // Notify data set has changed then final assert.
568993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutManager.expectLayouts(1);
569993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        mActivityRule.runOnUiThread(new Runnable() {
570993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
571993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            public void run() {
572993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                adapter.notifyDataSetChanged();
573993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
574993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        });
575993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutManager.waitForLayout(1);
576993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        checkForMainThreadException();
577993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // If hasStableIds, then no cached views should be recycled, otherwise just 1 should have
578993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // been recycled.
579993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        assertThat(cachedRecycleCount.get(), is(hasStableIds ? 0 : 1));
580993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    }
581993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
582993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    @Test
583993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    public void notifyDataSetChanged_hasStableIds_cachedViewsAreReusedForSamePositions()
584993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            throws Throwable {
585993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final Map<Integer, TestViewHolder> positionToViewHolderMap = new HashMap<>();
586993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final AtomicInteger layoutItemCount = new AtomicInteger();
587993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final AtomicBoolean inFirstBindViewHolderPass = new AtomicBoolean();
588993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
589993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final RecyclerView recyclerView = new RecyclerView(getActivity());
590993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        recyclerView.setItemViewCacheSize(5);
591993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
592993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        final TestAdapter adapter = new TestAdapter(10) {
593993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
5948a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder holder, int position) {
595993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                // Only track the top 5 positions that are going to be cached and then reused.
596993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                if (position >= 5) {
597993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    // If we are in the first phase, put the items in the map, if we are in the
598993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    // second phase, remove each one at the position and verify that it matches the
599993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    // provided ViewHolder.
600993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    if (inFirstBindViewHolderPass.get()) {
601993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                        positionToViewHolderMap.put(position, holder);
602993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    } else {
603993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                        TestViewHolder testViewHolder = positionToViewHolderMap.get(position);
604993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                        assertThat(holder, is(testViewHolder));
605993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                        positionToViewHolderMap.remove(position);
606993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    }
607993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                }
608993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                super.onBindViewHolder(holder, position);
609993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
610993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        };
611993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        adapter.setHasStableIds(true);
612993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        recyclerView.setAdapter(adapter);
613993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
614993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        TestLayoutManager testLayoutManager = new TestLayoutManager() {
615993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
616993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
617993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                try {
618993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    detachAndScrapAttachedViews(recycler);
619993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    layoutRange(recycler, 0, layoutItemCount.get());
620993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                } catch (Throwable t) {
621993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    postExceptionToInstrumentation(t);
622993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                } finally {
623993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                    layoutLatch.countDown();
624993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                }
625993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
626993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
627993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
628993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            public boolean supportsPredictiveItemAnimations() {
629993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                return false;
630993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
631993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        };
632993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        recyclerView.setLayoutManager(testLayoutManager);
633993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
634993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // First layout 10 items, then verify that the map has all 5 ViewHolders in it that will
635993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // be cached, and sanity check that the cache is empty.
636993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        inFirstBindViewHolderPass.set(true);
637993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutItemCount.set(10);
638993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.expectLayouts(1);
639993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        setRecyclerView(recyclerView, true, false);
640993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.waitForLayout(2);
641993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        checkForMainThreadException();
642993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        for (int i = 5; i < 10; i++) {
643993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            assertThat(positionToViewHolderMap.get(i), notNullValue());
644993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        }
645993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        assertThat(mRecyclerView.mRecycler.mCachedViews.size(), is(0));
646993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
647993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // Now only layout the first 5 items and verify that the cache has 5 items in it.
648993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutItemCount.set(5);
649993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.expectLayouts(1);
650993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        requestLayoutOnUIThread(mRecyclerView);
651993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.waitForLayout(1);
652993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        checkForMainThreadException();
653993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        assertThat(mRecyclerView.mRecycler.mCachedViews.size(), is(5));
654993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
655993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // Trigger notifyDataSetChanged and wait for layout.
656993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.expectLayouts(1);
657993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        mActivityRule.runOnUiThread(new Runnable() {
658993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            @Override
659993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            public void run() {
660993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard                adapter.notifyDataSetChanged();
661993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard            }
662993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        });
663993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.waitForLayout(1);
664993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        checkForMainThreadException();
665993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
666993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // Layout 10 items again, via the onBindViewholder method, check that each one of the views
667993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // returned from the recycler for positions >= 5 was in our cache of views, and verify that
668993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        // all 5 cached views were returned.
669993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        inFirstBindViewHolderPass.set(false);
670993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        layoutItemCount.set(10);
671993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.expectLayouts(1);
672993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        requestLayoutOnUIThread(mRecyclerView);
673993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        testLayoutManager.waitForLayout(1);
674993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        checkForMainThreadException();
675993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard        assertThat(positionToViewHolderMap.size(), is(0));
676993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    }
677993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard
678993fda7fed9bb30d2f16670623e66ac3f8c35c91shepshapard    @Test
67948560f9d06a8e43c190906d01d9abc55df93e899shepshapard    public void predictiveMeasuredCrashTest() throws Throwable {
680067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        final RecyclerView rv = new RecyclerView(getActivity());
681067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true) {
682067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            @Override
683067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            public void onAttachedToWindow(RecyclerView view) {
684067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                super.onAttachedToWindow(view);
685067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
686067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            }
687067ffce6a4d139c921daf943dc760ca120d80155Dake Gu
688067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            @Override
689067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
690067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                super.onDetachedFromWindow(view, recycler);
691067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
692067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            }
693067ffce6a4d139c921daf943dc760ca120d80155Dake Gu
694067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            @Override
695067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
696067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                    int widthSpec,
697067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                    int heightSpec) {
698067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                if (state.getItemCount() > 0) {
699067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                    // A typical LayoutManager will use a child view to measure the size.
700067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                    View v = recycler.getViewForPosition(0);
701067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                }
702067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                super.onMeasure(recycler, state, widthSpec, heightSpec);
703067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            }
70431df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard
70531df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard            @Override
70631df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard            public boolean isAutoMeasureEnabled() {
70731df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard                return false;
70831df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard            }
709067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        };
710067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        lm.setSupportsPredictive(true);
711067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        rv.setHasFixedSize(false);
712067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        final TestAdapter adapter = new TestAdapter(0);
713067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        rv.setAdapter(adapter);
714067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        rv.setLayoutManager(lm);
715067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        lm.expectLayouts(1);
716067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        setRecyclerView(rv);
717067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        lm.waitForLayout(2);
718067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        mActivityRule.runOnUiThread(new Runnable() {
719067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            @Override
720067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            public void run() {
721067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                ViewGroup parent = (ViewGroup) rv.getParent();
722067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                parent.removeView(rv);
723067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                // setting RV as child of LinearLayout using MATCH_PARENT will cause
724067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                // RV.onMeasure() being called twice before layout(). This may cause crash.
725067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                LinearLayout linearLayout = new LinearLayout(parent.getContext());
726067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                linearLayout.setOrientation(LinearLayout.VERTICAL);
727067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                parent.addView(linearLayout,
728067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                        ViewGroup.LayoutParams.WRAP_CONTENT,
729067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                        ViewGroup.LayoutParams.WRAP_CONTENT);
730067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                linearLayout.addView(rv, ViewGroup.LayoutParams.MATCH_PARENT,
731067ffce6a4d139c921daf943dc760ca120d80155Dake Gu                        ViewGroup.LayoutParams.WRAP_CONTENT);
732067ffce6a4d139c921daf943dc760ca120d80155Dake Gu
733067ffce6a4d139c921daf943dc760ca120d80155Dake Gu            }
734067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        });
735067ffce6a4d139c921daf943dc760ca120d80155Dake Gu
736067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        lm.expectLayouts(1);
737067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        adapter.addAndNotify(1);
738067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        lm.waitForLayout(2);
739067ffce6a4d139c921daf943dc760ca120d80155Dake Gu        checkForMainThreadException();
740067ffce6a4d139c921daf943dc760ca120d80155Dake Gu    }
741067ffce6a4d139c921daf943dc760ca120d80155Dake Gu
742067ffce6a4d139c921daf943dc760ca120d80155Dake Gu    @Test
74334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar    public void detachRvAndLayoutManagerProperly() throws Throwable {
74434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
74534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        final LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true) {
74634a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            @Override
74734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            public void onAttachedToWindow(RecyclerView view) {
74834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                super.onAttachedToWindow(view);
74934a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
75034a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
75134a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
75234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            @Override
75334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
75434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                super.onDetachedFromWindow(view, recycler);
75534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                assertThat(view.mLayout, is((RecyclerView.LayoutManager) this));
75634a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
75734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        };
75834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        final Runnable check = new Runnable() {
75934a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            @Override
76034a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            public void run() {
76134a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                assertThat("bound between the RV and the LM should be disconnected at the"
76234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                        + " same time", rv.mLayout == lm, is(lm.mRecyclerView == rv));
76334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
76434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        };
76534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        final AtomicInteger detachCounter = new AtomicInteger(0);
76634a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        rv.setAdapter(new TestAdapter(10) {
76734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            @Override
7688a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder holder,
76934a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                    int position) {
77034a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                super.onBindViewHolder(holder, position);
77134a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                holder.itemView.setFocusable(true);
77234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                holder.itemView.setFocusableInTouchMode(true);
77334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
77434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
77534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            @Override
77634a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            public void onViewDetachedFromWindow(TestViewHolder holder) {
77734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                super.onViewDetachedFromWindow(holder);
77834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                detachCounter.incrementAndGet();
77934a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                check.run();
78034a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
78134a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
78234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            @Override
7838a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull TestViewHolder holder) {
78434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                super.onViewRecycled(holder);
78534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                check.run();
78634a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
78734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        });
78834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        rv.setLayoutManager(lm);
78934a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        lm.expectLayouts(1);
79034a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        setRecyclerView(rv);
79134a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        lm.waitForLayout(2);
79234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        assertThat("test sanity", rv.getChildCount(), is(10));
79334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
79434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        final TestLayoutManager replacement = new LayoutAllLayoutManager(true);
79534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        replacement.expectLayouts(1);
79642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
79734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            @Override
79834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            public void run() {
79934a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                rv.setLayoutManager(replacement);
80034a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
80134a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        });
80234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        replacement.waitForLayout(2);
80334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        assertThat("test sanity", rv.getChildCount(), is(10));
80434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        assertThat("all initial views should be detached", detachCounter.get(), is(10));
80534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        checkForMainThreadException();
80634a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar    }
80734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
80834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar    @Test
809d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    public void focusSearchWithOtherFocusables() throws Throwable {
810d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        final LinearLayout container = new LinearLayout(getActivity());
811d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.setOrientation(LinearLayout.VERTICAL);
812d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
813d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        mRecyclerView = rv;
814d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        rv.setAdapter(new TestAdapter(10) {
815d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
8168a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder holder,
817d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                    int position) {
818d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                super.onBindViewHolder(holder, position);
819d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                holder.itemView.setFocusableInTouchMode(true);
820d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                holder.itemView.setLayoutParams(
821d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
8229c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                                ViewGroup.LayoutParams.WRAP_CONTENT));
823d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
824d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        });
825d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
826d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
827d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
828d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                detachAndScrapAttachedViews(recycler);
829d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                layoutRange(recycler, 0, 1);
830d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                layoutLatch.countDown();
831d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
832d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar
833d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Nullable
834d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
835d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            public View onFocusSearchFailed(View focused, int direction,
836d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                    RecyclerView.Recycler recycler,
837d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                    RecyclerView.State state) {
838c01998ea8076aed9902f0539ad7eb7625ec961a4Keyvan Amiri                int expectedDir = Build.VERSION.SDK_INT <= 15 ? View.FOCUS_DOWN :
839c01998ea8076aed9902f0539ad7eb7625ec961a4Keyvan Amiri                        View.FOCUS_FORWARD;
840c01998ea8076aed9902f0539ad7eb7625ec961a4Keyvan Amiri                assertEquals(expectedDir, direction);
841d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                assertEquals(1, getChildCount());
842d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                View child0 = getChildAt(0);
843d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                View view = recycler.getViewForPosition(1);
844d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                addView(view);
845d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                measureChild(view, 0, 0);
846d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                layoutDecorated(view, 0, child0.getBottom(), getDecoratedMeasuredWidth(view),
847d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                        child0.getBottom() + getDecoratedMeasuredHeight(view));
848d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                return view;
849d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
850d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri
851d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri            @Override
852d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
8539c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    RecyclerView.State state) {
854d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                super.scrollHorizontallyBy(dx, recycler, state);
855d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                // offset by -dx because the views translate opposite of the scrolling direction
856d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                mRecyclerView.offsetChildrenHorizontal(-dx);
857d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                return dx;
858d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri            }
859d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri
860d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri            @Override
861d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
8629c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    RecyclerView.State state) {
863d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                super.scrollVerticallyBy(dy, recycler, state);
864d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                // offset by -dy because the views translate opposite of the scrolling direction
865d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                mRecyclerView.offsetChildrenVertical(-dy);
866d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri                return dy;
867d0c99f1e61591601104f19c22e1656f8f0aec4bfKeyvan Amiri            }
86831df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard
86931df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard            @Override
87031df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard            public boolean isAutoMeasureEnabled() {
87131df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard                return true;
87231df934a5cd4bcaac517f313a9fc1a2639beaf9fshepshapard            }
873d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        };
874d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        rv.setLayoutManager(tlm);
875d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        TextView viewAbove = new TextView(getActivity());
876d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewAbove.setText("view above");
877d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewAbove.setFocusableInTouchMode(true);
878d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.addView(viewAbove);
879d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.addView(rv);
880d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        TextView viewBelow = new TextView(getActivity());
881d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewBelow.setText("view below");
882d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        viewBelow.setFocusableInTouchMode(true);
883d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        container.addView(viewBelow);
884d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        tlm.expectLayouts(1);
88542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
886d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            @Override
887d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            public void run() {
888d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                getActivity().getContainer().addView(container);
889d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar            }
890d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        });
891d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar
892d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        tlm.waitForLayout(2);
893d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        requestFocus(viewAbove, true);
894d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        assertTrue(viewAbove.hasFocus());
895d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        View newFocused = focusSearch(viewAbove, View.FOCUS_FORWARD);
896d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        assertThat(newFocused, sameInstance(rv.getChildAt(0)));
897d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        newFocused = focusSearch(rv.getChildAt(0), View.FOCUS_FORWARD);
898d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        assertThat(newFocused, sameInstance(rv.getChildAt(1)));
899d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    }
900d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar
901d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    @Test
9024510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxNoTranslation() throws Throwable {
9034510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
9044510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9054510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
9064510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
9074510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(10, 10, 30, 50)));
9084510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
9094510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
9104510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
9114510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
9124510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
9134510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxTranslateX() throws Throwable {
9144510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
9154510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9164510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
9174510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
918ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas                view.setTranslationX(10);
9194510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(20, 10, 40, 50)));
9204510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
9214510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
9224510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
9234510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
9244510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
9254510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxTranslateY() throws Throwable {
9264510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
9274510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9284510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
9294510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
930ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas                view.setTranslationY(10);
9314510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(10, 20, 30, 60)));
9324510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
9334510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
9344510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
9354510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
9364510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
9374510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxScaleX() throws Throwable {
9384510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
9394510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9404510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
9414510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
942ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas                view.setScaleX(2);
9434510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(0, 10, 40, 50)));
9444510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
9454510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
9464510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
9474510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
9484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
9494510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxScaleY() throws Throwable {
9504510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
9514510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9524510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
9534510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
954ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas                view.setScaleY(2);
9554510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(10, -10, 30, 70)));
9564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
9574510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
9584510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
9594510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
9604510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
9614510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxRotated() throws Throwable {
9624510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        transformedBoundingBoxTest(new ViewRunnable() {
9634510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9644510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run(View view) throws RuntimeException {
9654510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
966ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas                view.setRotation(90);
9674510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(getTransformedBoundingBox(view), is(new Rect(0, 20, 40, 40)));
9684510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
9694510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
9704510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
9714510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
9724510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
9734510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void boundingBoxRotatedWithDecorOffsets() throws Throwable {
9744510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
9754510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final TestAdapter adapter = new TestAdapter(1);
9764510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setAdapter(adapter);
9774510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
9784510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9794510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
9804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                    RecyclerView.State state) {
9814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                outRect.set(1, 2, 3, 4);
9824510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
9834510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
9844510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        TestLayoutManager layoutManager = new TestLayoutManager() {
9854510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
9864510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
9874510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                detachAndScrapAttachedViews(recycler);
9884510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                View view = recycler.getViewForPosition(0);
9894510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                addView(view);
9904510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.measure(
9914510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(20, View.MeasureSpec.EXACTLY),
9924510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(40, View.MeasureSpec.EXACTLY)
9934510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                );
9944510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                // trigger decor offsets calculation
9954510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                calculateItemDecorationsForChild(view, new Rect());
9964510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.layout(10, 10, 30, 50);
997ca03208c6ef5bd79af99309d0e14db4a238cb691Aurimas Liutikas                view.setRotation(90);
9984510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                assertThat(RecyclerViewLayoutTest.this.getTransformedBoundingBox(view),
9994510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        is(new Rect(-4, 19, 42, 43)));
10004510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
10014510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                layoutLatch.countDown();
10024510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
10034510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        };
10044510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setLayoutManager(layoutManager);
10054510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.expectLayouts(1);
10064510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        setRecyclerView(recyclerView);
10074510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.waitForLayout(2);
10084510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        checkForMainThreadException();
10094510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
10104510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
10114510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    private Rect getTransformedBoundingBox(View child) {
10124510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        Rect rect = new Rect();
10134510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        mRecyclerView.getLayoutManager().getTransformedBoundingBox(child, true, rect);
10144510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        return rect;
10154510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
10164510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
10174510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void transformedBoundingBoxTest(final ViewRunnable layout) throws Throwable {
10184510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
10194510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final TestAdapter adapter = new TestAdapter(1);
10204510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setAdapter(adapter);
10214510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        TestLayoutManager layoutManager = new TestLayoutManager() {
10224510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
10234510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
10244510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                detachAndScrapAttachedViews(recycler);
10254510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                View view = recycler.getViewForPosition(0);
10264510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                addView(view);
10274510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                view.measure(
10284510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(20, View.MeasureSpec.EXACTLY),
10294510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(40, View.MeasureSpec.EXACTLY)
10304510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                );
10314510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                layout.run(view);
10324510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                layoutLatch.countDown();
10334510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
10344510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        };
10354510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        recyclerView.setLayoutManager(layoutManager);
10364510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.expectLayouts(1);
10374510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        setRecyclerView(recyclerView);
10384510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        layoutManager.waitForLayout(2);
10394510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        checkForMainThreadException();
10404510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
10414510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
10424510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
10430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void flingFrozen() throws Throwable {
1044ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        testScrollFrozen(true);
1045ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
1046ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1047ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
10480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dragFrozen() throws Throwable {
1049ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        testScrollFrozen(false);
1050ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
1051ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
10523d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    @Test
10533d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    public void requestRectOnScreenWithScrollOffset() throws Throwable {
10543d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
10553d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final LayoutAllLayoutManager tlm = spy(new LayoutAllLayoutManager());
10563d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final int scrollY = 50;
10573d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
10583d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
10598a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public RecyclerView.ViewHolder onCreateViewHolder(
10608a11e6829c522aa1efcc903afa4c01d337082eabChris Craik                    @NonNull ViewGroup parent, int viewType) {
10613d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                View view = new View(parent.getContext());
10623d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                view.setScrollY(scrollY);
10638a11e6829c522aa1efcc903afa4c01d337082eabChris Craik                return new RecyclerView.ViewHolder(view) {};
10643d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            }
10653d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
10668a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {}
10673d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
10683d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            public int getItemCount() {
10693d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                return 1;
10703d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            }
10713d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        };
10723d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        recyclerView.setAdapter(adapter);
10733d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        recyclerView.setLayoutManager(tlm);
10743d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        tlm.expectLayouts(1);
10753d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        setRecyclerView(recyclerView);
10763d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        tlm.waitForLayout(1);
10773d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        final View child = recyclerView.getChildAt(0);
10783d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        assertThat(child.getScrollY(), CoreMatchers.is(scrollY));
107942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
10803d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            @Override
10813d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            public void run() {
10823d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                recyclerView.requestChildRectangleOnScreen(child, new Rect(3, 4, 5, 6), true);
10833d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                verify(tlm, times(1)).scrollVerticallyBy(eq(-46), any(RecyclerView.Recycler.class),
10843d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar                        any(RecyclerView.State.class));
10853d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            }
10863d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        });
10873d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    }
10883d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar
10892ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata    @Test
10902ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata    public void reattachAndScrollCrash() throws Throwable {
10912ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        final RecyclerView recyclerView = new RecyclerView(getActivity());
10922ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        final TestLayoutManager tlm = new TestLayoutManager() {
10932ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
10942ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
10952ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public boolean canScrollVertically() {
10962ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                return true;
10972ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
10982ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
10992ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
11002ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
11012ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                layoutRange(recycler, 0, Math.min(state.getItemCount(), 10));
11022ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
11032ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
11042ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
11052ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
11069c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    RecyclerView.State state) {
11072ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                // Access views in the state (that might have been deleted).
11082ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                for (int  i = 10; i < state.getItemCount(); i++) {
11092ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                    recycler.getViewForPosition(i);
11102ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                }
11112ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                return dy;
11122ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
11132ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        };
11142ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
11152ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        final TestAdapter adapter = new TestAdapter(12);
11162ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
11172ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        recyclerView.setAdapter(adapter);
11182ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        recyclerView.setLayoutManager(tlm);
11192ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
11202ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        setRecyclerView(recyclerView);
11212ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
112242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
11232ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            @Override
11242ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            public void run() {
1125a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().removeView(recyclerView);
1126a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().addView(recyclerView);
11272ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                try {
11282ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                    adapter.deleteAndNotify(1, adapter.getItemCount() - 1);
11292ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                } catch (Throwable throwable) {
11302ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                    postExceptionToInstrumentation(throwable);
11312ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                }
11322ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata                recyclerView.scrollBy(0, 10);
11332ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata            }
11342ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata        });
11352ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata    }
11362ee3e8c2bf8a9f02f0f9d487beb6463f172f1137susnata
1137ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    private void testScrollFrozen(boolean fling) throws Throwable {
1138ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        RecyclerView recyclerView = new RecyclerView(getActivity());
1139ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1140ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int horizontalScrollCount = 3;
1141ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int verticalScrollCount = 3;
1142ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int horizontalVelocity = 1000;
1143ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final int verticalVelocity = 1000;
1144ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger horizontalCounter = new AtomicInteger(horizontalScrollCount);
1145ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger verticalCounter = new AtomicInteger(verticalScrollCount);
1146ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestLayoutManager tlm = new TestLayoutManager() {
1147ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1148ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollHorizontally() {
1149ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
1150ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1151ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1152ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1153ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollVertically() {
1154ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
1155ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1156ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1157ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1158ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1159ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutRange(recycler, 0, 10);
1160ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutLatch.countDown();
1161ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1162ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1163ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1164ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
1165ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    RecyclerView.State state) {
1166ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                if (verticalCounter.get() > 0) {
1167ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    verticalCounter.decrementAndGet();
1168ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    return dy;
1169ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                }
1170ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return 0;
1171ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1172ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1173ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1174ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
1175ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    RecyclerView.State state) {
1176ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                if (horizontalCounter.get() > 0) {
1177ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    horizontalCounter.decrementAndGet();
1178ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    return dx;
1179ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                }
1180ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return 0;
1181ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1182ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        };
1183ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter = new TestAdapter(100);
1184ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setAdapter(adapter);
1185ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setLayoutManager(tlm);
1186ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.expectLayouts(1);
1187ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setRecyclerView(recyclerView);
1188ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.waitForLayout(2);
1189ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1190ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
1191ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1192ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        if (fling) {
1193ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            assertFalse("fling should be blocked", fling(horizontalVelocity, verticalVelocity));
1194ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        } else { // drag
11950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TouchUtils.dragViewTo(getInstrumentation(), recyclerView,
1196ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    Gravity.LEFT | Gravity.TOP,
1197ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    mRecyclerView.getWidth() / 2, mRecyclerView.getHeight() / 2);
1198ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        }
1199ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's horizontal scroll cb must not run", horizontalScrollCount,
1200ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                horizontalCounter.get());
1201ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's vertical scroll cb must not run", verticalScrollCount,
1202ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                verticalCounter.get());
1203ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1204ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
1205ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1206ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        if (fling) {
1207ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            assertTrue("fling should be started", fling(horizontalVelocity, verticalVelocity));
1208ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        } else { // drag
12090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TouchUtils.dragViewTo(getInstrumentation(), recyclerView,
1210ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    Gravity.LEFT | Gravity.TOP,
1211ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    mRecyclerView.getWidth() / 2, mRecyclerView.getHeight() / 2);
1212ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        }
1213ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's horizontal scroll cb must finishes", 0, horizontalCounter.get());
1214ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("rv's vertical scroll cb must finishes", 0, verticalCounter.get());
1215ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
1216ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1217ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
12184be551523e8e36b5a1d480af456bfb392416882cYigit Boyar    public void testFocusSearchAfterChangedData() throws Throwable {
12194be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
12204be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
12214be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            @Override
12224be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
12234be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                layoutRange(recycler, 0, 2);
12244be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                layoutLatch.countDown();
12254be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            }
12264be551523e8e36b5a1d480af456bfb392416882cYigit Boyar
12274be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            @Nullable
12284be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            @Override
12294be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            public View onFocusSearchFailed(View focused, int direction,
12309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    RecyclerView.Recycler recycler,
12319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                    RecyclerView.State state) {
12324be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                try {
123332b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                    recycler.getViewForPosition(state.getItemCount() - 1);
12344be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                } catch (Throwable t) {
12354be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                    postExceptionToInstrumentation(t);
12364be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                }
12374be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                return null;
12384be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            }
12394be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        };
12404be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        recyclerView.setLayoutManager(tlm);
12414be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        final TestAdapter adapter = new TestAdapter(10) {
12424be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            @Override
12438a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder holder, int position) {
12444be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                super.onBindViewHolder(holder, position);
12454be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                holder.itemView.setFocusable(false);
12464be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                holder.itemView.setFocusableInTouchMode(false);
12474be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            }
12484be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        };
12494be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        recyclerView.setAdapter(adapter);
12504be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        tlm.expectLayouts(1);
12514be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        setRecyclerView(recyclerView);
12524be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        tlm.waitForLayout(1);
125342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
12544be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            @Override
12554be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            public void run() {
12564be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                adapter.mItems.remove(9);
12574be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                adapter.notifyItemRemoved(9);
12584be551523e8e36b5a1d480af456bfb392416882cYigit Boyar                recyclerView.focusSearch(recyclerView.getChildAt(1), View.FOCUS_DOWN);
12594be551523e8e36b5a1d480af456bfb392416882cYigit Boyar            }
12604be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        });
12614be551523e8e36b5a1d480af456bfb392416882cYigit Boyar        checkForMainThreadException();
12624be551523e8e36b5a1d480af456bfb392416882cYigit Boyar    }
12634be551523e8e36b5a1d480af456bfb392416882cYigit Boyar
12644be551523e8e36b5a1d480af456bfb392416882cYigit Boyar    @Test
12653e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar    public void testFocusSearchWithRemovedFocusedItem() throws Throwable {
12663e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
12673e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        recyclerView.setItemAnimator(null);
12683e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        TestLayoutManager tlm = new LayoutAllLayoutManager();
12693e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        recyclerView.setLayoutManager(tlm);
12703e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        final TestAdapter adapter = new TestAdapter(10) {
12713e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar            @Override
12728a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder holder, int position) {
12733e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar                super.onBindViewHolder(holder, position);
12743e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar                holder.itemView.setFocusable(true);
12753e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar                holder.itemView.setFocusableInTouchMode(true);
12763e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar            }
12773e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        };
12783e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        recyclerView.setAdapter(adapter);
12793e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        tlm.expectLayouts(1);
12803e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        setRecyclerView(recyclerView);
12813e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        tlm.waitForLayout(1);
12823e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        final RecyclerView.ViewHolder toFocus = recyclerView.findViewHolderForAdapterPosition(9);
12833e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        requestFocus(toFocus.itemView, true);
12843e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        assertThat("test sanity", toFocus.itemView.hasFocus(), is(true));
128542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
12863e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar            @Override
12873e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar            public void run() {
12883e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar                adapter.mItems.remove(9);
12893e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar                adapter.notifyItemRemoved(9);
12903e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar                recyclerView.focusSearch(toFocus.itemView, View.FOCUS_DOWN);
12913e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar            }
12923e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        });
12933e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar        checkForMainThreadException();
12943e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar    }
12953e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar
12963e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar
12973e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar    @Test
12987bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar    public void  testFocusSearchFailFrozen() throws Throwable {
1299ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        RecyclerView recyclerView = new RecyclerView(getActivity());
13007bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar        final CountDownLatch focusLatch = new CountDownLatch(1);
1301ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger focusSearchCalled = new AtomicInteger(0);
1302ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestLayoutManager tlm = new TestLayoutManager() {
1303ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1304ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollHorizontally() {
1305ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
1306ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1307ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1308ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1309ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollVertically() {
1310ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
1311ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1312ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1313ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1314ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1315ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutRange(recycler, 0, 10);
1316ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutLatch.countDown();
1317ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1318ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1319ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1320121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar            public View onFocusSearchFailed(View focused, int direction,
1321121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    RecyclerView.Recycler recycler, RecyclerView.State state) {
1322ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                focusSearchCalled.addAndGet(1);
13237bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar                focusLatch.countDown();
1324ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return null;
1325ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1326ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        };
1327ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter = new TestAdapter(100);
1328ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setAdapter(adapter);
1329ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setLayoutManager(tlm);
1330ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.expectLayouts(1);
1331ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setRecyclerView(recyclerView);
1332ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.waitForLayout(2);
1333ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final View c = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
1334959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", requestFocus(c, true));
1335959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", c.hasFocus());
1336ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
13374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        focusSearch(recyclerView, c, View.FOCUS_DOWN);
1338ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("onFocusSearchFailed should not be called when layout is frozen",
1339ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                0, focusSearchCalled.get());
1340ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
1341d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        focusSearch(c, View.FOCUS_DOWN);
13427bb9b2507dc5ed5fa2609d6b8ecf7d274f7bb619Yigit Boyar        assertTrue(focusLatch.await(2, TimeUnit.SECONDS));
1343ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals(1, focusSearchCalled.get());
1344ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
1345ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1346d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar    public View focusSearch(final ViewGroup parent, final View focused, final int direction)
13474143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            throws Throwable {
1348d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        final View[] result = new View[1];
134942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
13504143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            @Override
13514143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            public void run() {
1352d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar                result[0] = parent.focusSearch(focused, direction);
13534143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            }
13544143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        });
1355d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar        return result[0];
13564143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
13574143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
1358ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
13590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void frozenAndChangeAdapter() throws Throwable {
1360ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        RecyclerView recyclerView = new RecyclerView(getActivity());
1361ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1362ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        final AtomicInteger focusSearchCalled = new AtomicInteger(0);
1363ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestLayoutManager tlm = new TestLayoutManager() {
1364ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1365ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollHorizontally() {
1366ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
1367ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1368ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1369ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1370ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public boolean canScrollVertically() {
1371ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return true;
1372ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1373ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1374ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1375ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1376ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutRange(recycler, 0, 10);
1377ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                layoutLatch.countDown();
1378ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1379ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1380ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            @Override
1381ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
1382ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                    RecyclerView.State state) {
1383ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                focusSearchCalled.addAndGet(1);
1384ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                return null;
1385ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu            }
1386ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        };
1387ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter = new TestAdapter(100);
1388ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setAdapter(adapter);
1389ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        recyclerView.setLayoutManager(tlm);
1390ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.expectLayouts(1);
1391ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setRecyclerView(recyclerView);
1392ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.waitForLayout(2);
1393ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1394ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
1395ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter2 = new TestAdapter(1000);
1396ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setAdapter(adapter2);
1397ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertFalse(recyclerView.isLayoutFrozen());
1398ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertSame(adapter2, recyclerView.getAdapter());
1399ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1400ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
1401ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        TestAdapter adapter3 = new TestAdapter(1000);
1402ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        swapAdapter(adapter3, true);
1403ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertFalse(recyclerView.isLayoutFrozen());
1404ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertSame(adapter3, recyclerView.getAdapter());
1405ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
1406ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1407ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    @Test
14089eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu    public void moveAndUpdateCachedViews() throws Throwable {
14099eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        final RecyclerView recyclerView = new RecyclerView(getActivity());
14109eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        recyclerView.setItemViewCacheSize(3);
14119eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        recyclerView.setItemAnimator(null);
14129eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        final TestAdapter adapter = new TestAdapter(1000);
14139eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        final CountDownLatch layoutLatch = new CountDownLatch(1);
14149eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        LinearLayoutManager tlm = new LinearLayoutManager(recyclerView.getContext()) {
14159eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            @Override
14169eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
14179eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                super.onLayoutChildren(recycler, state);
14189eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                layoutLatch.countDown();
14199eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            }
14209eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        };
14219eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        tlm.setItemPrefetchEnabled(false);
14229eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        recyclerView.setLayoutManager(tlm);
14239eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        recyclerView.setAdapter(adapter);
14249eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        setRecyclerView(recyclerView);
14259eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        // wait first layout pass
14269eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        layoutLatch.await();
14279eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        // scroll and hide 0 and 1
14289eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        mActivityRule.runOnUiThread(new Runnable() {
14299eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            @Override
14309eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            public void run() {
14319eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                recyclerView.smoothScrollBy(0, recyclerView.getChildAt(2).getTop() + 5);
14329eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            }
14339eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        });
14349eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        waitForIdleScroll(recyclerView);
14359eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        assertNull(recyclerView.findViewHolderForAdapterPosition(0));
14369eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        assertNull(recyclerView.findViewHolderForAdapterPosition(1));
14379eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        // swap 1 and 0 and update 0
14389eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        mActivityRule.runOnUiThread(new Runnable() {
14399eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            @Override
14409eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            public void run() {
14419eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                try {
14429eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                    // swap 1 and 0
14439eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                    adapter.moveInUIThread(1, 0);
14449eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                    adapter.notifyItemMoved(1, 0);
14459eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                    // update 0
14469eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                    adapter.mItems.get(0).mText = "updated";
14479eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                    adapter.notifyItemChanged(0);
14489eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                } catch (Throwable throwable) {
14499eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                    postExceptionToInstrumentation(throwable);
14509eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu                }
14519eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu            }
14529eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        });
14539eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        // scroll back to 0
14549eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        smoothScrollToPosition(0);
14559eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        waitForIdleScroll(recyclerView);
14569eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        TestViewHolder vh = (TestViewHolder) recyclerView.findViewHolderForAdapterPosition(0);
14579eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        // assert updated right item
14589eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        assertTrue((((TextView) (vh.itemView)).getText()).toString().contains("updated"));
14599eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu        checkForMainThreadException();
14609eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu    }
14619eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu
14629eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu    @Test
14638fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreChanged() throws Throwable {
14648fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
14658fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
14668fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
14678fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeChanged(3, 0);
14688fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
14698fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
14708fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
14718fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
14728fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
14738fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreChangedWithPayload() throws Throwable {
14748fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
14758fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
14768fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
14778fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeChanged(0, 0, new Object());
14788fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
14798fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
14808fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
14818fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
14828fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
14838fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreAdded() throws Throwable {
14848fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
14858fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
14868fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
14878fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeInserted(3, 0);
14888fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
14898fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
14908fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
14918fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
14928fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
14938fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIf0ItemsAreRemoved() throws Throwable {
14948fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
14958fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
14968fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
14978fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemRangeRemoved(3, 0);
14988fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
14998fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
15008fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
15018fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
15028fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
15038fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void noLayoutIfItemMovedIntoItsOwnPlace() throws Throwable {
15048fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        unnecessaryNotifyEvents(new AdapterRunnable() {
15058fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
15068fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run(TestAdapter adapter) throws Throwable {
15078fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                adapter.notifyItemMoved(3, 3);
15088fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
15098fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
15108fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
15118fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
15128fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    public void unnecessaryNotifyEvents(final AdapterRunnable action) throws Throwable {
15138fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
15148fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        final TestAdapter adapter = new TestAdapter(5);
15158fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
15168fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
15178fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
15188fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                super.onLayoutChildren(recycler, state);
15198fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                layoutLatch.countDown();
15208fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
15218fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        };
15228fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        recyclerView.setLayoutManager(tlm);
15238fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        recyclerView.setAdapter(adapter);
15248fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.expectLayouts(1);
15258fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        setRecyclerView(recyclerView);
15268fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.waitForLayout(1);
15278fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        // ready
15288fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.expectLayouts(1);
152942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
15308fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            @Override
15318fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            public void run() {
15328fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                try {
15338fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                    action.run(adapter);
15348fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                } catch (Throwable throwable) {
15358fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                    postExceptionToInstrumentation(throwable);
15368fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar                }
15378fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar            }
15388fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        });
15398fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        tlm.assertNoLayout("dummy event should not trigger a layout", 1);
15408fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar        checkForMainThreadException();
15418fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    }
15428fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar
15438fd6812c1cc120bb2637526ca4c641fbb01f7b0cYigit Boyar    @Test
15440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollToPositionCallback() throws Throwable {
15452e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
15462e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        class TestRecyclerView extends RecyclerView {
15472e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
15482e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            private CountDownLatch mDrawLatch;
15492e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
15502e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            TestRecyclerView(Context context) {
15512e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard                super(context);
15522e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            }
15532e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
15542e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            public void expectDraws(int count) {
15552e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard                mDrawLatch = new CountDownLatch(count);
15562e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            }
15572e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
15582e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            public void waitForDraw(int seconds) throws InterruptedException {
15592e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard                mDrawLatch.await(seconds, TimeUnit.SECONDS);
15602e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            }
15612e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
15622e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            @Override
15632e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            public void onDraw(Canvas c) {
15642e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard                super.onDraw(c);
15652e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard                if (mDrawLatch != null) {
15662e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard                    mDrawLatch.countDown();
15672e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard                }
15682e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard            }
15692e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        }
15702e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
15712e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        TestRecyclerView recyclerView = new TestRecyclerView(getActivity());
15724965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
15734965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            int scrollPos = RecyclerView.NO_POSITION;
15741a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
15754965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            @Override
15764965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
15774965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                layoutLatch.countDown();
15784965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                if (scrollPos == RecyclerView.NO_POSITION) {
15794965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                    layoutRange(recycler, 0, 10);
15804965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                } else {
15814965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                    layoutRange(recycler, scrollPos, scrollPos + 10);
15824965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                }
15834965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            }
15841a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
15854965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            @Override
15864965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            public void scrollToPosition(int position) {
15874965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                scrollPos = position;
15884965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                requestLayout();
15894965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            }
15904965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        };
15914965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        recyclerView.setLayoutManager(tlm);
15924965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        TestAdapter adapter = new TestAdapter(100);
15934965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        recyclerView.setAdapter(adapter);
15944965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        final AtomicInteger rvCounter = new AtomicInteger(0);
15954965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        final AtomicInteger viewGroupCounter = new AtomicInteger(0);
159632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar
159732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
15984965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            @Override
15994965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
16004965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                rvCounter.incrementAndGet();
16014965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar            }
16024965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        });
16033f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar
160432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        getRecyclerViewContainer().getViewTreeObserver().addOnScrollChangedListener(
160532b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                new ViewTreeObserver.OnScrollChangedListener() {
160632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                    @Override
160732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                    public void onScrollChanged() {
160832b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                        viewGroupCounter.incrementAndGet();
160932b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                    }
161032b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                });
161132b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar
16122e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        recyclerView.expectDraws(1);
161332b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        tlm.expectLayouts(1);
16144965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        setRecyclerView(recyclerView);
16154965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.waitForLayout(2);
16162e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        recyclerView.waitForDraw(2);
16173f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("RV on scroll should be called for initialization", 1, rvCounter.get());
16183f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("VTO on scroll should be called for initialization", 1,
16194965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar                viewGroupCounter.get());
16202e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
16212e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        recyclerView.expectDraws(1);
16224965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.expectLayouts(1);
1623ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
1624ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        scrollToPosition(3);
1625ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        tlm.assertNoLayout("scrollToPosition should be ignored", 2);
1626ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
16274965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        scrollToPosition(3);
16284965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.waitForLayout(2);
16292e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        recyclerView.waitForDraw(2);
16303f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("RV on scroll should be called", 2, rvCounter.get());
16313f937299abacc5ff1be7a5ffa91793396aca0a57Yigit Boyar        assertEquals("VTO on scroll should be called", 2, viewGroupCounter.get());
16322e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard
16332e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        recyclerView.expectDraws(1);
16344965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.expectLayouts(1);
16354965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        requestLayoutOnUIThread(recyclerView);
16364965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        tlm.waitForLayout(2);
16372e11ef95c2414d294561ddd7711c2e3eb355b3d8shepshapard        recyclerView.waitForDraw(2);
16384965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        assertEquals("on scroll should NOT be called", 2, rvCounter.get());
16394965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar        assertEquals("on scroll should NOT be called", 2, viewGroupCounter.get());
1640e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
1641e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
1642e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
164356b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar    public void scrollCallbackFromEmptyToSome() throws Throwable {
164456b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar        scrollCallbackOnVisibleRangeChange(1, new int[]{0, 0}, new int[]{0, 1});
164556b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar    }
164656b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar
164756b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar    @Test
164856b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar    public void scrollCallbackOnVisibleRangeExpand() throws Throwable {
1649e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 5}, new int[]{3, 6});
1650e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
1651e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
1652e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
165356b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar    public void scrollCallbackOnVisibleRangeShrink() throws Throwable {
1654e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 6}, new int[]{3, 5});
1655e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
1656e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
1657e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
165856b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar    public void scrollCallbackOnVisibleRangeExpand2() throws Throwable {
1659e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 5}, new int[]{2, 5});
1660e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
1661e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
1662e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    @Test
166356b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar    public void scrollCallbackOnVisibleRangeShrink2() throws Throwable {
1664e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        scrollCallbackOnVisibleRangeChange(10, new int[]{3, 6}, new int[]{2, 6});
1665e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    }
1666e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
1667e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar    private void scrollCallbackOnVisibleRangeChange(int itemCount, final int[] beforeRange,
1668e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            final int[] afterRange) throws Throwable {
166956b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity()) {
167056b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar            @Override
167156b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar            void dispatchLayout() {
167256b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar                super.dispatchLayout();
167356b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar                ((TestLayoutManager) getLayoutManager()).layoutLatch.countDown();
167456b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar            }
167556b45ed1fae2d4a7cd68c8d38bd61518147bc57eYigit Boyar        };
1676e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        final AtomicBoolean beforeState = new AtomicBoolean(true);
1677e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
1678e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            @Override
1679e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1680e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar                detachAndScrapAttachedViews(recycler);
1681e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar                int[] range = beforeState.get() ? beforeRange : afterRange;
1682e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar                layoutRange(recycler, range[0], range[1]);
1683e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar            }
1684e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        };
1685e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        recyclerView.setLayoutManager(tlm);
1686e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        final TestAdapter adapter = new TestAdapter(itemCount);
1687e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        recyclerView.setAdapter(adapter);
1688e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.expectLayouts(1);
1689e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        setRecyclerView(recyclerView);
1690e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.waitForLayout(1);
1691e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar
1692e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        RecyclerView.OnScrollListener mockListener = mock(RecyclerView.OnScrollListener.class);
1693e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        recyclerView.addOnScrollListener(mockListener);
1694e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        verify(mockListener, never()).onScrolled(any(RecyclerView.class), anyInt(), anyInt());
16954965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar
1696e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.expectLayouts(1);
1697e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        beforeState.set(false);
1698e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        requestLayoutOnUIThread(recyclerView);
1699e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        tlm.waitForLayout(2);
1700e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        checkForMainThreadException();
1701e4f37e5de85662368bdc74f71b60bf402e6937dfYigit Boyar        verify(mockListener).onScrolled(recyclerView, 0, 0);
17024965046abec63014d2a5e7f72f6c9e11b73a4907Yigit Boyar    }
1703670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1704be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17052e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar    public void addItemOnScroll() throws Throwable {
17062e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
17072e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final AtomicInteger start = new AtomicInteger(0);
17082e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
17092e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            @Override
17102e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
17112e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                layoutRange(recycler, start.get(), start.get() + 10);
17122e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                layoutLatch.countDown();
17132e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            }
17142e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        };
17152e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        recyclerView.setLayoutManager(tlm);
17162e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final TestAdapter adapter = new TestAdapter(100);
17172e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        recyclerView.setAdapter(adapter);
17182e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.expectLayouts(1);
17192e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        setRecyclerView(recyclerView);
17202e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.waitForLayout(1);
17212e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final Throwable[] error = new Throwable[1];
17222e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        final AtomicBoolean calledOnScroll = new AtomicBoolean(false);
17232e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
17242e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            @Override
17252e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
17262e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                super.onScrolled(recyclerView, dx, dy);
17272e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                calledOnScroll.set(true);
17282e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                try {
17292e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                    adapter.addAndNotify(5, 20);
17302e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                } catch (Throwable throwable) {
17312e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                    error[0] = throwable;
17322e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar                }
17332e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar            }
17342e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        });
17352e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        start.set(4);
17362e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        MatcherAssert.assertThat("test sanity", calledOnScroll.get(), CoreMatchers.is(false));
17372e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.expectLayouts(1);
17382e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        requestLayoutOnUIThread(recyclerView);
17392e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        tlm.waitForLayout(2);
17402e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        checkForMainThreadException();
17412e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        MatcherAssert.assertThat("test sanity", calledOnScroll.get(), CoreMatchers.is(true));
17422e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar        MatcherAssert.assertThat(error[0], CoreMatchers.nullValue());
17432e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar    }
17442e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar
17452e77ca33cc242ec5750cfc11824c215f8d7ffc22Yigit Boyar    @Test
17460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionEqual() throws Throwable {
1747670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 3, 1000, 1000);
1748670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1749670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1750be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreVertical() throws Throwable {
1752670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(2, 3, 1000, 1000);
1753670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1754670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1755be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreHorizontal() throws Throwable {
1757670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 2, 1000, 1000);
1758670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1759670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1760be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollHorizontalOnly() throws Throwable {
1762670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 0, 1000, 0);
1763670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1764670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1765be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollVerticalOnly() throws Throwable {
1767670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(0, 3, 0, 1000);
1768670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1769670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1770be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionEqualReverse() throws Throwable {
1772670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 3, -1000, -1000);
1773670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1774670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1775be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreVerticalReverse() throws Throwable {
1777670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(2, 3, -1000, -1000);
1778670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1779670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1780be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollInBothDirectionMoreHorizontalReverse() throws Throwable {
1782670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 2, -1000, -1000);
1783670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1784670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1785be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollHorizontalOnlyReverse() throws Throwable {
1787670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(3, 0, -1000, 0);
1788670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1789670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1790be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
17910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollVerticalOnlyReverse() throws Throwable {
1792670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        scrollInBothDirection(0, 3, 0, -1000);
1793670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1794670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1795670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    public void scrollInBothDirection(int horizontalScrollCount, int verticalScrollCount,
1796670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            int horizontalVelocity, int verticalVelocity)
1797670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            throws Throwable {
1798670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1799670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicInteger horizontalCounter = new AtomicInteger(horizontalScrollCount);
1800670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicInteger verticalCounter = new AtomicInteger(verticalScrollCount);
1801670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
1802670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1803670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollHorizontally() {
1804670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return true;
1805670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1806670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1807670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1808670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollVertically() {
1809670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return true;
1810670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1811670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1812670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1813670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1814670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutRange(recycler, 0, 10);
1815670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutLatch.countDown();
1816670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1817670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1818670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1819670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
1820670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
1821670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                if (verticalCounter.get() > 0) {
1822670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    verticalCounter.decrementAndGet();
1823670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    return dy;
1824670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                }
1825670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return 0;
1826670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1827670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1828670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1829670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
1830670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
1831670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                if (horizontalCounter.get() > 0) {
1832670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    horizontalCounter.decrementAndGet();
1833670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    return dx;
1834670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                }
1835670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                return 0;
1836670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1837670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        };
1838670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        TestAdapter adapter = new TestAdapter(100);
1839670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setAdapter(adapter);
1840670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setLayoutManager(tlm);
1841670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.expectLayouts(1);
1842670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        setRecyclerView(recyclerView);
1843670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.waitForLayout(2);
1844670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        assertTrue("test sanity, fling must run", fling(horizontalVelocity, verticalVelocity));
1845670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        assertEquals("rv's horizontal scroll cb must run " + horizontalScrollCount + " times'", 0,
1846670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                horizontalCounter.get());
1847670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        assertEquals("rv's vertical scroll cb must run " + verticalScrollCount + " times'", 0,
1848670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                verticalCounter.get());
1849670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1850670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1851be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
18520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dragHorizontal() throws Throwable {
1853cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_HORIZONTAL);
1854670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1855670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1856be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
18570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dragVertical() throws Throwable {
1858cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_VERTICAL);
1859670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1860670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1861be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
18620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void flingHorizontal() throws Throwable {
1863cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_HORIZONTAL | FLAG_FLING);
1864670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1865670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1866be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
18670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void flingVertical() throws Throwable {
1868cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_VERTICAL | FLAG_FLING);
1869670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
1870670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
187176daed103193a1756535d1f59b165e98e1d17445Chris Banes    @SuppressWarnings("WrongConstant")
1872f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    @Test
18730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void nestedDragVertical() throws Throwable {
187476daed103193a1756535d1f59b165e98e1d17445Chris Banes        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
187576daed103193a1756535d1f59b165e98e1d17445Chris Banes        getActivity().getContainer().setNestedScrollingDelegate(nsp);
187676daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Scroll and expect the RV to not scroll
1877cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_VERTICAL, 0);
1878f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes
187976daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the touch nested scroll was started and finished
188076daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
188176daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
188276daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
188376daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
188476daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
188576daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
188676daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onStopNestedScroll(eq(mRecyclerView), eq(ViewCompat.TYPE_TOUCH));
188776daed103193a1756535d1f59b165e98e1d17445Chris Banes
188876daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the non-touch events were dispatched by the fling settle
188976daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
189076daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
189176daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
189276daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
189376daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
189476daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
189576daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
189676daed103193a1756535d1f59b165e98e1d17445Chris Banes
189776daed103193a1756535d1f59b165e98e1d17445Chris Banes    @SuppressWarnings("WrongConstant")
1898f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    @Test
18990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void nestedDragHorizontal() throws Throwable {
190076daed103193a1756535d1f59b165e98e1d17445Chris Banes        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
190176daed103193a1756535d1f59b165e98e1d17445Chris Banes        getActivity().getContainer().setNestedScrollingDelegate(nsp);
190276daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Scroll and expect the RV to not scroll
1903cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        scrollInOtherOrientationTest(FLAG_HORIZONTAL, 0);
1904670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
190576daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the touch nested scroll was started and finished
190676daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
190776daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
190876daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
190976daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
191076daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
191176daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
191276daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onStopNestedScroll(eq(mRecyclerView), eq(ViewCompat.TYPE_TOUCH));
191376daed103193a1756535d1f59b165e98e1d17445Chris Banes
191476daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the non-touch events were dispatched by the fling settle
191576daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
191676daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
191776daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
191876daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
191976daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
192076daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
192176daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
192276daed103193a1756535d1f59b165e98e1d17445Chris Banes
192376daed103193a1756535d1f59b165e98e1d17445Chris Banes    @SuppressWarnings("WrongConstant")
1924d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes    @Test
192576daed103193a1756535d1f59b165e98e1d17445Chris Banes    public void nestedFlingVertical() throws Throwable {
192676daed103193a1756535d1f59b165e98e1d17445Chris Banes        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
192776daed103193a1756535d1f59b165e98e1d17445Chris Banes        getActivity().getContainer().setNestedScrollingDelegate(nsp);
192876daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Fling and expect the RV to not scroll
192976daed103193a1756535d1f59b165e98e1d17445Chris Banes        scrollInOtherOrientationTest(FLAG_VERTICAL | FLAG_FLING, FLAG_FLING);
193076daed103193a1756535d1f59b165e98e1d17445Chris Banes
193176daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the touch nested scroll was not started
193276daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
193376daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
193476daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
193576daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_TOUCH));
193676daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
193776daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
193876daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_TOUCH);
193976daed103193a1756535d1f59b165e98e1d17445Chris Banes
194076daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the non-touch nested scroll was started and finished
194176daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
194276daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
194376daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
194476daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
194576daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
194676daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
194776daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_NON_TOUCH);
194876daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
194976daed103193a1756535d1f59b165e98e1d17445Chris Banes
195076daed103193a1756535d1f59b165e98e1d17445Chris Banes    @SuppressWarnings("WrongConstant")
1951d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes    @Test
195276daed103193a1756535d1f59b165e98e1d17445Chris Banes    public void nestedFlingHorizontal() throws Throwable {
195376daed103193a1756535d1f59b165e98e1d17445Chris Banes        final NestedScrollingParent2 nsp = spy(new FullyConsumingNestedScroller());
195476daed103193a1756535d1f59b165e98e1d17445Chris Banes        getActivity().getContainer().setNestedScrollingDelegate(nsp);
195576daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Fling and expect the RV to not scroll
195676daed103193a1756535d1f59b165e98e1d17445Chris Banes        scrollInOtherOrientationTest(FLAG_HORIZONTAL | FLAG_FLING, FLAG_FLING);
195776daed103193a1756535d1f59b165e98e1d17445Chris Banes
195876daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the touch nested scroll was not started
195976daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
196076daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
196176daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
196276daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_TOUCH));
196376daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
196476daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
196576daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, never()).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_TOUCH);
196676daed103193a1756535d1f59b165e98e1d17445Chris Banes
196776daed103193a1756535d1f59b165e98e1d17445Chris Banes        // Verify that the non-touch nested scroll was started and finished
196876daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
196976daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
197076daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
197176daed103193a1756535d1f59b165e98e1d17445Chris Banes                eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
197276daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
197376daed103193a1756535d1f59b165e98e1d17445Chris Banes                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
197476daed103193a1756535d1f59b165e98e1d17445Chris Banes        verify(nsp, times(1)).onStopNestedScroll(mRecyclerView, ViewCompat.TYPE_NON_TOUCH);
1975d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes    }
1976d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes
1977cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private void scrollInOtherOrientationTest(int flags)
1978f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes            throws Throwable {
1979364ca611da0789aefae36126a8e302abaefa0d48Yigit Boyar        scrollInOtherOrientationTest(flags, flags);
1980f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes    }
1981f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes
1982cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    private void scrollInOtherOrientationTest(final int flags, int expectedFlags) throws Throwable {
1983670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
1984670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicBoolean scrolledHorizontal = new AtomicBoolean(false);
1985670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicBoolean scrolledVertical = new AtomicBoolean(false);
1986f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes
1987f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        final TestLayoutManager tlm = new TestLayoutManager() {
1988670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1989670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollHorizontally() {
1990cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                return (flags & FLAG_HORIZONTAL) != 0;
1991670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1992670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1993670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1994670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public boolean canScrollVertically() {
1995cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                return (flags & FLAG_VERTICAL) != 0;
1996670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
1997670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
1998670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
1999670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2000670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutRange(recycler, 0, 10);
2001670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                layoutLatch.countDown();
2002670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
2003670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
2004670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
2005670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
2006670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
2007670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                scrolledVertical.set(true);
2008f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes                return super.scrollVerticallyBy(dy, recycler, state);
2009670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
2010670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
2011670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
2012670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
2013670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                    RecyclerView.State state) {
2014670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                scrolledHorizontal.set(true);
2015f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes                return super.scrollHorizontallyBy(dx, recycler, state);
2016670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
2017670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        };
2018670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        TestAdapter adapter = new TestAdapter(100);
2019670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setAdapter(adapter);
2020670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        recyclerView.setLayoutManager(tlm);
2021670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.expectLayouts(1);
2022670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        setRecyclerView(recyclerView);
2023670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        tlm.waitForLayout(2);
2024cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        if ( (flags & FLAG_FLING) != 0 ) {
2025cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar            int flingVelocity = (mRecyclerView.getMaxFlingVelocity() +
2026cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                    mRecyclerView.getMinFlingVelocity()) / 2;
2027cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar            assertEquals("fling started", (expectedFlags & FLAG_FLING) != 0,
2028cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                    fling(flingVelocity, flingVelocity));
2029f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        } else { // drag
20300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            TouchUtils.dragViewTo(getInstrumentation(), recyclerView, Gravity.LEFT | Gravity.TOP,
2031cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                    mRecyclerView.getWidth() / 2, mRecyclerView.getHeight() / 2);
2032670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        }
2033f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        assertEquals("horizontally scrolled: " + tlm.mScrollHorizontallyAmount,
2034cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                (expectedFlags & FLAG_HORIZONTAL) != 0, scrolledHorizontal.get());
2035f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes        assertEquals("vertically scrolled: " + tlm.mScrollVerticallyAmount,
2036cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar                (expectedFlags & FLAG_VERTICAL) != 0, scrolledVertical.get());
2037670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
2038670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
2039670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    private boolean fling(final int velocityX, final int velocityY) throws Throwable {
2040670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        final AtomicBoolean didStart = new AtomicBoolean(false);
204142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2042670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            @Override
2043670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            public void run() {
2044670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                boolean result = mRecyclerView.fling(velocityX, velocityY);
2045670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar                didStart.set(result);
2046670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            }
2047670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        });
2048670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        if (!didStart.get()) {
2049670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar            return false;
2050670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        }
2051959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        waitForIdleScroll(mRecyclerView);
2052670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar        return true;
2053670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar    }
2054670a72b9301fa53a763344036e4d3ff1995f36dfYigit Boyar
2055959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    private void assertPendingUpdatesAndLayoutTest(final AdapterRunnable runnable) throws Throwable {
2056959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2057959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        TestLayoutManager layoutManager = new DumbLayoutManager();
2058959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
2059959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        setupBasic(recyclerView, layoutManager, testAdapter, false);
2060959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        layoutManager.expectLayouts(1);
206142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2062da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
2063da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            public void run() {
2064959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                try {
2065959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    runnable.run(testAdapter);
2066959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                } catch (Throwable throwable) {
2067959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    fail("runnable has thrown an exception");
2068959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                }
2069da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar                assertTrue(mRecyclerView.hasPendingAdapterUpdates());
2070da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
2071da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
2072959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        layoutManager.waitForLayout(1);
2073da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        assertFalse(mRecyclerView.hasPendingAdapterUpdates());
2074959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        checkForMainThreadException();
2075da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
2076da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
2077da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    private void setupBasic(RecyclerView recyclerView, TestLayoutManager tlm,
2078da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            TestAdapter adapter, boolean waitForFirstLayout) throws Throwable {
2079da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        recyclerView.setLayoutManager(tlm);
2080da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        recyclerView.setAdapter(adapter);
2081da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        if (waitForFirstLayout) {
2082da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            tlm.expectLayouts(1);
2083da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            setRecyclerView(recyclerView);
2084da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            tlm.waitForLayout(1);
2085da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        } else {
2086da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            setRecyclerView(recyclerView);
2087da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        }
2088da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
2089da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
2090b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viverette    @Suppress
2091b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viverette    @FlakyTest(bugId = 33949798)
2092be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
2093b549f757dead5a9afda72a41bdece3bdcea3751aAlan Viverette    @LargeTest
20940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void hasPendingUpdatesBeforeFirstLayout() throws Throwable {
2095da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2096da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestLayoutManager layoutManager = new DumbLayoutManager();
2097da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
2098da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        setupBasic(recyclerView, layoutManager, testAdapter, false);
2099da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        assertTrue(mRecyclerView.hasPendingAdapterUpdates());
2100da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
2101da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
2102be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
21030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void noPendingUpdatesAfterLayout() throws Throwable {
2104da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2105da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestLayoutManager layoutManager = new DumbLayoutManager();
2106da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
2107da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        setupBasic(recyclerView, layoutManager, testAdapter, true);
2108da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        assertFalse(mRecyclerView.hasPendingAdapterUpdates());
2109da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
2110da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
2111be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
2112959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsRemoved() throws Throwable {
2113959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
2114da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
2115959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
2116959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.deleteAndNotify(1, 1);
2117da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
2118da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
2119959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
2120959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
2121959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsInserted() throws Throwable {
2122959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
2123da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
2124959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
2125959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.addAndNotify(2, 1);
2126da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
2127da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
2128959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
2129959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
2130959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsMoved() throws Throwable {
2131959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
2132da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
2133959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
2134959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.moveItem(2, 3, true);
2135da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
2136da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
2137959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
2138959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
2139959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterItemIsChanged() throws Throwable {
2140959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
2141da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
2142959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) throws Throwable {
2143959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                testAdapter.changeAndNotify(2, 1);
2144da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
2145da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
2146959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    }
2147959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    @Test
2148959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar    public void hasPendingUpdatesAfterDataSetIsChanged() throws Throwable {
2149959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertPendingUpdatesAndLayoutTest(new AdapterRunnable() {
2150da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            @Override
2151959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar            public void run(TestAdapter testAdapter) {
2152959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                mRecyclerView.getAdapter().notifyDataSetChanged();
2153da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
2154da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        });
2155da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
2156da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar
215732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
2158be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
21590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void transientStateRecycleViaAdapter() throws Throwable {
2160e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        transientStateRecycleTest(true, false);
2161e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
2162e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
216332b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
2164be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
21650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void transientStateRecycleViaTransientStateCleanup() throws Throwable {
2166e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        transientStateRecycleTest(false, true);
2167e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
2168e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
216932b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
2170be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
21710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void transientStateDontRecycle() throws Throwable {
2172e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        transientStateRecycleTest(false, false);
2173e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
2174e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
2175e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    public void transientStateRecycleTest(final boolean succeed, final boolean unsetTransientState)
2176e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            throws Throwable {
217732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        final List<View> failedToRecycle = new ArrayList<>();
217832b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        final List<View> recycled = new ArrayList<>();
2179e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10) {
2180e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            @Override
21818a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public boolean onFailedToRecycleView(@NonNull TestViewHolder holder) {
2182e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                failedToRecycle.add(holder.itemView);
2183e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                if (unsetTransientState) {
2184e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    setHasTransientState(holder.itemView, false);
2185e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
2186e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                return succeed;
2187e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            }
2188e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
2189e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            @Override
21908a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull TestViewHolder holder) {
2191e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                recycled.add(holder.itemView);
2192e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                super.onViewRecycled(holder);
2193e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            }
2194e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        };
2195e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
2196e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            @Override
2197e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2198e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                if (getChildCount() == 0) {
2199e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    detachAndScrapAttachedViews(recycler);
2200e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    layoutRange(recycler, 0, 5);
2201e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                } else {
2202e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    removeAndRecycleAllViews(recycler);
2203e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
2204e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                if (layoutLatch != null) {
2205e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    layoutLatch.countDown();
2206e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
2207e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            }
2208e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        };
2209e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2210e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        recyclerView.setAdapter(testAdapter);
2211e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        recyclerView.setLayoutManager(tlm);
2212e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        recyclerView.setItemAnimator(null);
2213e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        setRecyclerView(recyclerView);
2214e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        getInstrumentation().waitForIdleSync();
2215e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        // make sure we have enough views after this position so that we'll receive the on recycled
2216e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        // callback
2217e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        View view = recyclerView.getChildAt(3);//this has to be greater than def cache size.
2218e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        setHasTransientState(view, true);
2219e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        tlm.expectLayouts(1);
2220e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        requestLayoutOnUIThread(recyclerView);
2221e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        tlm.waitForLayout(2);
2222e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
2223e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        assertTrue(failedToRecycle.contains(view));
2224e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        assertEquals(succeed || unsetTransientState, recycled.contains(view));
2225e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar    }
2226e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar
2227be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
22280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionInvalidation() throws Throwable {
2229e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
2230e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        final TestAdapter adapter = new TestAdapter(10);
2231e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        final TestLayoutManager tlm = new TestLayoutManager() {
2232e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            @Override
2233e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2234e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                layoutRange(recycler, 0, state.getItemCount());
2235e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                layoutLatch.countDown();
2236e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            }
2237e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        };
2238e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        recyclerView.setAdapter(adapter);
2239e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        recyclerView.setLayoutManager(tlm);
2240e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        tlm.expectLayouts(1);
2241e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        setRecyclerView(recyclerView);
2242e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        tlm.waitForLayout(1);
224342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2244e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            @Override
2245e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            public void run() {
22461a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                for (int i = 0; i < tlm.getChildCount(); i++) {
2247e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                    assertNotSame("adapter positions should not be undefined",
2248e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            recyclerView.getChildAdapterPosition(tlm.getChildAt(i)),
2249e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            RecyclerView.NO_POSITION);
2250e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                }
2251e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                adapter.notifyDataSetChanged();
22521a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                for (int i = 0; i < tlm.getChildCount(); i++) {
2253e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                    assertSame("adapter positions should be undefined",
2254e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            recyclerView.getChildAdapterPosition(tlm.getChildAt(i)),
2255e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                            RecyclerView.NO_POSITION);
2256e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                }
2257e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            }
2258e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        });
2259e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar    }
2260e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar
2261be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
22620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsBasic() throws Throwable {
2263115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(null);
2264115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
2265115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2266be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
22670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsRemoveItems() throws Throwable {
2268115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
2269115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2270115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
2271115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.deleteAndNotify(3, 4);
2272115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2273115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
2274115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
2275115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2276be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
22770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsRemoveItemsBefore() throws Throwable {
2278115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
2279115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2280115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
2281115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.deleteAndNotify(0, 1);
2282115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2283115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
2284115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
2285115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2286be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
22870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsAddItemsBefore() throws Throwable {
2288115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
2289115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2290115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
2291115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.addAndNotify(0, 5);
2292115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2293115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
2294115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
2295115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2296be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
22970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsAddItemsInside() throws Throwable {
2298115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
2299115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2300115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
2301115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.addAndNotify(3, 2);
2302115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2303115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
2304115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
2305115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2306be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
23070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsMoveItems() throws Throwable {
2308115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
2309115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2310115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
2311115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.moveAndNotify(3, 5);
2312115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2313115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
2314115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
2315115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2316be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
23170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterPositionsNotifyDataSetChanged() throws Throwable {
2318115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        adapterPositionsTest(new AdapterRunnable() {
2319115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2320115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run(TestAdapter adapter) throws Throwable {
2321115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.mItems.clear();
23221a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                for (int i = 0; i < 20; i++) {
2323115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    adapter.mItems.add(new Item(i, "added item"));
2324115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                }
2325115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                adapter.notifyDataSetChanged();
2326115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2327115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
2328115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
2329115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
233032b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN) // transientState is API 16
2331be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
23320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void avoidLeakingRecyclerViewIfViewIsNotRecycled() throws Throwable {
2333619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final AtomicBoolean failedToRecycle = new AtomicBoolean(false);
233432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        final AtomicInteger recycledViewCount = new AtomicInteger(0);
2335619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
2336619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
2337619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
2338619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2339619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                detachAndScrapAttachedViews(recycler);
2340619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutRange(recycler, 0, state.getItemCount());
2341619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutLatch.countDown();
2342619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
2343619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        };
2344619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestAdapter adapter = new TestAdapter(10) {
2345619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
2346619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public boolean onFailedToRecycleView(
23478a11e6829c522aa1efcc903afa4c01d337082eabChris Craik                    @NonNull TestViewHolder holder) {
2348619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                failedToRecycle.set(true);
2349619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                return false;
2350619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
235132b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar
235232b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar            @Override
23538a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull TestViewHolder holder) {
235432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                recycledViewCount.incrementAndGet();
235532b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                super.onViewRecycled(holder);
235632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar            }
2357619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        };
2358619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setAdapter(adapter);
2359619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setLayoutManager(tlm);
2360619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
2361619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        setRecyclerView(rv);
2362619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(1);
2363619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final RecyclerView.ViewHolder vh = rv.getChildViewHolder(rv.getChildAt(0));
236442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2365619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
2366619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public void run() {
2367619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                ViewCompat.setHasTransientState(vh.itemView, true);
2368619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
2369619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        });
2370619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
2371619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        adapter.deleteAndNotify(0, 10);
2372619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(2);
2373619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final CountDownLatch animationsLatch = new CountDownLatch(1);
2374619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.getItemAnimator().isRunning(
2375619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
2376619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    @Override
2377619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    public void onAnimationsFinished() {
2378619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                        animationsLatch.countDown();
2379619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    }
2380619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                });
2381619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertTrue(animationsLatch.await(2, TimeUnit.SECONDS));
238232b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        assertThat(recycledViewCount.get(), is(9));
2383619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertTrue(failedToRecycle.get());
2384619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertNull(vh.mOwnerRecyclerView);
2385619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        checkForMainThreadException();
2386619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar    }
2387619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar
2388be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
23890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void avoidLeakingRecyclerViewViaViewHolder() throws Throwable {
2390619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
2391619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
2392619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            @Override
2393619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2394619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                detachAndScrapAttachedViews(recycler);
2395619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutRange(recycler, 0, state.getItemCount());
2396619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                layoutLatch.countDown();
2397619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
2398619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        };
2399619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
2400619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setAdapter(adapter);
2401619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.setLayoutManager(tlm);
2402619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
2403619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        setRecyclerView(rv);
2404619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(1);
2405619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final RecyclerView.ViewHolder vh = rv.getChildViewHolder(rv.getChildAt(0));
2406619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.expectLayouts(1);
2407619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        adapter.deleteAndNotify(0, 10);
2408619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        tlm.waitForLayout(2);
2409619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        final CountDownLatch animationsLatch = new CountDownLatch(1);
2410619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        rv.getItemAnimator().isRunning(
2411619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
2412619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    @Override
2413619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    public void onAnimationsFinished() {
2414619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                        animationsLatch.countDown();
2415619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    }
2416619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                });
2417619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertTrue(animationsLatch.await(2, TimeUnit.SECONDS));
2418619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        assertNull(vh.mOwnerRecyclerView);
2419619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        checkForMainThreadException();
2420619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar    }
2421619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar
2422bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    @Test
2423bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    public void duplicateAdapterPositionTest() throws Throwable {
2424bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
2425bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final TestLayoutManager tlm = new TestLayoutManager() {
2426bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            @Override
2427bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2428bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                detachAndScrapAttachedViews(recycler);
2429bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                layoutRange(recycler, 0, state.getItemCount());
2430bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                if (!state.isPreLayout()) {
2431bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    while (!recycler.getScrapList().isEmpty()) {
2432bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        RecyclerView.ViewHolder viewHolder = recycler.getScrapList().get(0);
2433bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        addDisappearingView(viewHolder.itemView, 0);
2434bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    }
2435bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                }
2436bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                layoutLatch.countDown();
2437bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            }
2438bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2439bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            @Override
2440bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
2441bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                return true;
2442bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            }
2443bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        };
2444bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final DefaultItemAnimator animator = new DefaultItemAnimator();
2445bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        animator.setSupportsChangeAnimations(true);
2446bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        animator.setChangeDuration(10000);
2447bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        testAdapter.setHasStableIds(true);
2448bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final TestRecyclerView recyclerView = new TestRecyclerView(getActivity());
2449bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.setLayoutManager(tlm);
2450bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.setAdapter(testAdapter);
2451bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.setItemAnimator(animator);
2452bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2453bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.expectLayouts(1);
2454bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        setRecyclerView(recyclerView);
2455bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.waitForLayout(2);
2456bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2457bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.expectLayouts(2);
2458bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        testAdapter.mItems.get(2).mType += 2;
2459bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final int itemId = testAdapter.mItems.get(2).mId;
2460bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        testAdapter.changeAndNotify(2, 1);
2461bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        tlm.waitForLayout(2);
2462bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
246342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2464bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            @Override
2465bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            public void run() {
2466bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat("test sanity", recyclerView.getChildCount(), CoreMatchers.is(11));
2467bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // now mangle the order and run the test
2468bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                RecyclerView.ViewHolder hidden = null;
2469bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                RecyclerView.ViewHolder updated = null;
2470bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                for (int i = 0; i < recyclerView.getChildCount(); i ++) {
2471bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    View view = recyclerView.getChildAt(i);
2472bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view);
2473bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    if (vh.getAdapterPosition() == 2) {
2474bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        if (mRecyclerView.mChildHelper.isHidden(view)) {
2475bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            assertThat(hidden, CoreMatchers.nullValue());
2476bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            hidden = vh;
2477bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        } else {
2478bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            assertThat(updated, CoreMatchers.nullValue());
2479bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                            updated = vh;
2480bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        }
2481bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    }
2482bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                }
2483bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(hidden, CoreMatchers.notNullValue());
2484bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(updated, CoreMatchers.notNullValue());
2485bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
24865999379d7d2e21d55c6816e6d0dd0c6291ba37a4shepshapard                mRecyclerView.startInterceptRequestLayout();
2487bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2488bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // first put the hidden child back
2489bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                int index1 = mRecyclerView.indexOfChild(hidden.itemView);
2490bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                int index2 = mRecyclerView.indexOfChild(updated.itemView);
2491bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                if (index1 < index2) {
2492bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    // swap views
2493bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                    swapViewsAtIndices(recyclerView, index1, index2);
2494bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                }
2495bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(tlm.findViewByPosition(2), CoreMatchers.sameInstance(updated.itemView));
2496bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2497bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForAdapterPosition(2),
2498bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
2499bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForLayoutPosition(2),
2500bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
2501bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForItemId(itemId),
2502bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
2503bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2504bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // now swap back
2505bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                swapViewsAtIndices(recyclerView, index1, index2);
2506bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2507bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(tlm.findViewByPosition(2), CoreMatchers.sameInstance(updated.itemView));
2508bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForAdapterPosition(2),
2509bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
2510bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForLayoutPosition(2),
2511bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
2512bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForItemId(itemId),
2513bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(updated));
2514bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2515bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                // now remove updated. re-assert fallback to the hidden one
2516bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                tlm.removeView(updated.itemView);
2517bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2518bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(tlm.findViewByPosition(2), CoreMatchers.nullValue());
2519bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForAdapterPosition(2),
2520bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(hidden));
2521bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForLayoutPosition(2),
2522bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(hidden));
2523bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                assertThat(recyclerView.findViewHolderForItemId(itemId),
2524bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar                        CoreMatchers.sameInstance(hidden));
2525bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            }
2526bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        });
2527bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2528bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    }
2529bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2530bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    private void swapViewsAtIndices(TestRecyclerView recyclerView, int index1, int index2) {
2531bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (index1 == index2) {
2532bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            return;
2533bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
2534bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (index2 < index1) {
2535bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            int tmp = index1;
2536bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            index1 = index2;
2537bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            index2 = tmp;
2538bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
2539bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final View v1 = recyclerView.getChildAt(index1);
2540bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        final View v2 = recyclerView.getChildAt(index2);
2541bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        boolean v1Hidden = recyclerView.mChildHelper.isHidden(v1);
2542bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        boolean v2Hidden = recyclerView.mChildHelper.isHidden(v2);
254332b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        // must un-hide before swap otherwise bucket indices will become invalid.
2544bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v1Hidden) {
2545bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.unhide(v1);
2546bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
2547bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v2Hidden) {
2548bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.unhide(v2);
2549bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
2550bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.detachViewFromParent(index2);
2551bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.attachViewToParent(v2, index1, v2.getLayoutParams());
2552bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.detachViewFromParent(index1 + 1);
2553bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        recyclerView.attachViewToParent(v1, index2, v1.getLayoutParams());
2554bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2555bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v1Hidden) {
2556bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.hide(v1);
2557bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
2558bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        if (v2Hidden) {
2559bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            mRecyclerView.mChildHelper.hide(v2);
2560bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
2561bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    }
2562bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
2563115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    public void adapterPositionsTest(final AdapterRunnable adapterChanges) throws Throwable {
2564115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
2565115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
2566115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2567115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2568115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                try {
2569115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    layoutRange(recycler, Math.min(state.getItemCount(), 2)
2570115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            , Math.min(state.getItemCount(), 7));
2571115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    layoutLatch.countDown();
2572115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                } catch (Throwable t) {
2573115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    postExceptionToInstrumentation(t);
2574115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                }
2575115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2576115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        };
2577115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
2578115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        recyclerView.setLayoutManager(tlm);
2579115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        recyclerView.setAdapter(testAdapter);
2580115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        tlm.expectLayouts(1);
2581115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        setRecyclerView(recyclerView);
2582115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        tlm.waitForLayout(1);
258342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2584115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            @Override
2585115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            public void run() {
2586115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                try {
2587115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    final int count = recyclerView.getChildCount();
258832b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                    Map<View, Integer> layoutPositions = new HashMap<>();
2589115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    assertTrue("test sanity", count > 0);
25901a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    for (int i = 0; i < count; i++) {
2591115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        View view = recyclerView.getChildAt(i);
2592115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        TestViewHolder vh = (TestViewHolder) recyclerView.getChildViewHolder(view);
2593115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        int index = testAdapter.mItems.indexOf(vh.mBoundItem);
2594115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        assertEquals("should be able to find VH with adapter position " + index, vh,
2595115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                recyclerView.findViewHolderForAdapterPosition(index));
2596115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        assertEquals("get adapter position should return correct index", index,
2597115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                vh.getAdapterPosition());
2598115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        layoutPositions.put(view, vh.mPosition);
2599115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    }
2600115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    if (adapterChanges != null) {
2601115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        adapterChanges.run(testAdapter);
2602115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        for (int i = 0; i < count; i++) {
2603115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            View view = recyclerView.getChildAt(i);
2604115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            TestViewHolder vh = (TestViewHolder) recyclerView
2605115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    .getChildViewHolder(view);
2606115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            int index = testAdapter.mItems.indexOf(vh.mBoundItem);
2607115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            if (index >= 0) {
2608115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                assertEquals("should be able to find VH with adapter position "
26091a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                                                + index, vh,
2610115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                        recyclerView.findViewHolderForAdapterPosition(index));
2611115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            }
2612115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            assertSame("get adapter position should return correct index", index,
2613115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    vh.getAdapterPosition());
2614115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            assertSame("should be able to find view with layout position",
2615115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    vh, mRecyclerView.findViewHolderForLayoutPosition(
2616115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                            layoutPositions.get(view)));
2617115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        }
2618115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2619115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    }
2620115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
2621115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                } catch (Throwable t) {
2622115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    postExceptionToInstrumentation(t);
2623115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                }
2624115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            }
2625115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        });
2626115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        checkForMainThreadException();
2627e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
2628e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
2629be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
26300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForSmoothScroll() throws Throwable {
2631bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
2632bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
2633bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2634bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
2635bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
2636bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
2637bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
2638bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
2639bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
2640bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(2);
264132b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
2642bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2643bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
2644bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
2645bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
2646bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2647bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
264842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2649bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2650bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
2651bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.smoothScrollBy(0, 500);
2652bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2653bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2654bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
2655bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
2656bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
2657bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
2658bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
2659bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2660be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
26610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForSmoothScrollWithStop() throws Throwable {
2662bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
2663bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
2664bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2665bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
2666bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
2667bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
2668bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
2669bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
2670bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
2671bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(1);
267232b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
2673bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2674bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
2675bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
2676bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
2677bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2678bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
267942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2680bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2681bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
2682bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.smoothScrollBy(0, 500);
2683bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2684bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2685bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
268642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2687bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2688bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
2689bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.stopScroll();
2690bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2691bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2692bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
2693bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
2694bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
2695bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
2696bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
2697bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2698be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
26990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForFling() throws Throwable {
2700bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
2701bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
2702bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2703bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
2704bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
2705bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
2706bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
2707bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
2708bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
2709bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(2);
271032b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
2711bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2712bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
2713bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
2714bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
2715bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2716bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2717bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final ViewConfiguration vc = ViewConfiguration.get(getActivity());
2718bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final float fling = vc.getScaledMinimumFlingVelocity()
2719bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                + (vc.getScaledMaximumFlingVelocity() - vc.getScaledMinimumFlingVelocity()) * .1f;
272042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2721bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2722bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
2723bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.fling(0, Math.round(fling));
2724bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2725bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2726bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
2727bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
2728bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
2729bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
2730bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
2731bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2732be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
27330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateForFlingWithStop() throws Throwable {
2734bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
2735bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
2736bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2737bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
2738bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
2739bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
2740bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
2741bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
2742bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
2743bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final CountDownLatch latch = new CountDownLatch(1);
274432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
2745bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2746bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
2747bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
2748bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                latch.countDown();
2749bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2750bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2751bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final ViewConfiguration vc = ViewConfiguration.get(getActivity());
2752bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final float fling = vc.getScaledMinimumFlingVelocity()
2753bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                + (vc.getScaledMaximumFlingVelocity() - vc.getScaledMinimumFlingVelocity()) * .8f;
275442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2755bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2756bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
2757bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.fling(0, Math.round(fling));
2758bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2759bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2760bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        latch.await(5, TimeUnit.SECONDS);
276142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2762bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2763bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
2764bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                mRecyclerView.stopScroll();
2765bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2766bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2767bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
2768bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_SETTLING]);
2769bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
2770bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_DRAGGING]);
2771bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
2772bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2773be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
27740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollStateDrag() throws Throwable {
2775bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
2776bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager();
2777bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
2778bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setAdapter(testAdapter);
2779bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        recyclerView.setLayoutManager(tlm);
2780bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        setRecyclerView(recyclerView);
2781bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
2782bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(SCROLL_STATE_IDLE, recyclerView.getScrollState());
2783bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        final int[] stateCnts = new int[10];
278432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
2785bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2786bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
2787bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                stateCnts[newState] = stateCnts[newState] + 1;
2788bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2789bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2790bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        drag(mRecyclerView, 0, 0, 0, 500, 5);
2791bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(0, stateCnts[SCROLL_STATE_SETTLING]);
2792bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_IDLE]);
2793bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        assertEquals(1, stateCnts[SCROLL_STATE_DRAGGING]);
2794bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
2795bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2796bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    public void drag(ViewGroup view, float fromX, float toX, float fromY, float toY,
2797bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            int stepCount) throws Throwable {
2798bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        long downTime = SystemClock.uptimeMillis();
2799bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        long eventTime = SystemClock.uptimeMillis();
2800bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2801bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float y = fromY;
2802bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float x = fromX;
2803bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2804bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float yStep = (toY - fromY) / stepCount;
2805bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        float xStep = (toX - fromX) / stepCount;
2806bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2807bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
2808bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                MotionEvent.ACTION_DOWN, x, y, 0);
2809bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        sendTouch(view, event);
2810bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        for (int i = 0; i < stepCount; ++i) {
2811bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            y += yStep;
2812bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            x += xStep;
2813bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            eventTime = SystemClock.uptimeMillis();
2814bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
2815bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            sendTouch(view, event);
2816bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
2817bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2818bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        eventTime = SystemClock.uptimeMillis();
2819bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
2820bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        sendTouch(view, event);
2821bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        getInstrumentation().waitForIdleSync();
2822bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
2823bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2824bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    private void sendTouch(final ViewGroup view, final MotionEvent event) throws Throwable {
282542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2826bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            @Override
2827bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            public void run() {
2828bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                if (view.onInterceptTouchEvent(event)) {
2829bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                    view.onTouchEvent(event);
2830bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                }
2831bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            }
2832bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        });
2833bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar    }
2834bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
2835be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
28360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleScrap() throws Throwable {
283766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recycleScrapTest(false);
283866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        removeRecyclerView();
283966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recycleScrapTest(true);
284066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar    }
284166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar
284266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar    public void recycleScrapTest(final boolean useRecycler) throws Throwable {
284366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
284466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        final AtomicBoolean test = new AtomicBoolean(false);
284566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
284666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar            @Override
284766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2848e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                ViewInfoStore infoStore = mRecyclerView.mViewInfoStore;
284966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                if (test.get()) {
285066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                    try {
285166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        detachAndScrapAttachedViews(recycler);
285266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        for (int i = recycler.getScrapList().size() - 1; i >= 0; i--) {
285366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            if (useRecycler) {
285466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                recycler.recycleView(recycler.getScrapList().get(i).itemView);
285566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            } else {
285666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                removeAndRecycleView(recycler.getScrapList().get(i).itemView,
285766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                        recycler);
285866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            }
285966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        }
2860e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                        if (infoStore.mOldChangedHolders != null) {
2861e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                            for (int i = infoStore.mOldChangedHolders.size() - 1; i >= 0; i--) {
286266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                if (useRecycler) {
286366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                    recycler.recycleView(
2864e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                            infoStore.mOldChangedHolders.valueAt(i).itemView);
286566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                } else {
286666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                    removeAndRecycleView(
2867e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                            infoStore.mOldChangedHolders.valueAt(i).itemView,
2868e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                            recycler);
286966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                                }
287066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            }
287166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        }
287266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        assertEquals("no scrap should be left over", 0, recycler.getScrapCount());
287366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        assertEquals("pre layout map should be empty", 0,
2874e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                InfoStoreTrojan.sizeOfPreLayout(infoStore));
287566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        assertEquals("post layout map should be empty", 0,
2876e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                InfoStoreTrojan.sizeOfPostLayout(infoStore));
2877e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                        if (infoStore.mOldChangedHolders != null) {
287866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                            assertEquals("post old change map should be empty", 0,
2879e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar                                    infoStore.mOldChangedHolders.size());
288066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        }
288166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                    } catch (Throwable t) {
288266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                        postExceptionToInstrumentation(t);
288366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                    }
288466a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar
288566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                }
288666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                layoutRange(recycler, 0, 5);
288766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                layoutLatch.countDown();
288866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar                super.onLayoutChildren(recycler, state);
288966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar            }
289066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        };
289166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
289266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recyclerView.setAdapter(testAdapter);
289366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        recyclerView.setLayoutManager(lm);
2894121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        ((SimpleItemAnimator)recyclerView.getItemAnimator()).setSupportsChangeAnimations(true);
289566a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.expectLayouts(1);
289666a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        setRecyclerView(recyclerView);
289766a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.waitForLayout(2);
289866a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        test.set(true);
289966a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.expectLayouts(1);
290066a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        testAdapter.changeAndNotify(3, 1);
290166a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        lm.waitForLayout(2);
290266a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar        checkForMainThreadException();
290366a5670ac0e58e1d08f6af57b13648c1ab9df441Yigit Boyar    }
290422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
2905be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
29064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    public void aAccessRecyclerOnOnMeasureWithPredictive() throws Throwable {
29075cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        accessRecyclerOnOnMeasureTest(true);
29085cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar    }
29095cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
2910be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
29114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    public void accessRecyclerOnOnMeasureWithoutPredictive() throws Throwable {
29124143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        accessRecyclerOnOnMeasureTest(false);
29134143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
29144143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
29154143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    @Test
29160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void smoothScrollWithRemovedItemsAndRemoveItem() throws Throwable {
2917cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        smoothScrollTest(true);
2918cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    }
2919cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar
2920cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar    @Test
29210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void smoothScrollWithRemovedItems() throws Throwable {
2922ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        smoothScrollTest(false);
2923ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    }
2924ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2925ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    public void smoothScrollTest(final boolean removeItem) throws Throwable {
2926ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final LinearSmoothScroller[] lss = new LinearSmoothScroller[1];
2927ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final CountDownLatch calledOnStart = new CountDownLatch(1);
2928ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final CountDownLatch calledOnStop = new CountDownLatch(1);
2929ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final int visibleChildCount = 10;
2930ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
2931ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            int start = 0;
2932ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2933ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2934ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
2935ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                super.onLayoutChildren(recycler, state);
2936ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                layoutRange(recycler, start, visibleChildCount);
2937ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                layoutLatch.countDown();
2938ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2939ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2940ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2941ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
2942ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    RecyclerView.State state) {
2943ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                start++;
2944ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                if (DEBUG) {
2945ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    Log.d(TAG, "on scroll, remove and recycling. start:" + start + ", cnt:"
2946ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            + visibleChildCount);
2947ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                }
2948ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                removeAndRecycleAllViews(recycler);
2949c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                layoutRange(recycler, start,
2950c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                        Math.max(state.getItemCount(), start + visibleChildCount));
2951ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                return dy;
2952ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2953ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2954ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2955ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public boolean canScrollVertically() {
2956ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                return true;
2957ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2958ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2959ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2960ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
2961ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    int position) {
2962ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                LinearSmoothScroller linearSmoothScroller =
2963ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        new LinearSmoothScroller(recyclerView.getContext()) {
2964ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            @Override
2965678ce8206279d0ea2e6425fbb02ec9183d93d8edAurimas Liutikas                            public PointF computeScrollVectorForPosition(int targetPosition) {
2966678ce8206279d0ea2e6425fbb02ec9183d93d8edAurimas Liutikas                                return new PointF(0, 1);
2967ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
2968ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2969ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            @Override
2970ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            protected void onStart() {
2971ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                super.onStart();
2972ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                calledOnStart.countDown();
2973ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
2974ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
2975ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            @Override
2976ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            protected void onStop() {
2977ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                super.onStop();
2978ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                calledOnStop.countDown();
2979ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
2980ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        };
2981ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                linearSmoothScroller.setTargetPosition(position);
2982ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                lss[0] = linearSmoothScroller;
2983ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                startSmoothScroll(linearSmoothScroller);
2984ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
2985ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        };
2986ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
2987ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        TestAdapter testAdapter = new TestAdapter(500);
2988ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        rv.setLayoutManager(lm);
2989ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        rv.setAdapter(testAdapter);
2990ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        lm.expectLayouts(1);
2991ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        setRecyclerView(rv);
2992ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        lm.waitForLayout(1);
2993ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        // regular scroll
2994ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        final int targetPosition = visibleChildCount * (removeItem ? 30 : 4);
299542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
2996ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            @Override
2997ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            public void run() {
2998ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                rv.smoothScrollToPosition(targetPosition);
2999ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
3000ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        });
3001ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        if (DEBUG) {
3002ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            Log.d(TAG, "scrolling to target position " + targetPosition);
3003ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        }
3004ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        assertTrue("on start should be called very soon", calledOnStart.await(2, TimeUnit.SECONDS));
3005ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        if (removeItem) {
3006ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            final int newTarget = targetPosition - 10;
3007ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            testAdapter.deleteAndNotify(newTarget + 1, testAdapter.getItemCount() - newTarget - 1);
300890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            final CountDownLatch targetCheck = new CountDownLatch(1);
300942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas            mActivityRule.runOnUiThread(new Runnable() {
3010ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                @Override
3011ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                public void run() {
3012ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    ViewCompat.postOnAnimationDelayed(rv, new Runnable() {
3013ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        @Override
3014ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        public void run() {
3015ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            try {
3016ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                assertEquals("scroll position should be updated to next available",
3017ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                        newTarget, lss[0].getTargetPosition());
3018ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            } catch (Throwable t) {
3019ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                                postExceptionToInstrumentation(t);
3020ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                            }
302190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                            targetCheck.countDown();
3022ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                        }
302390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    }, 50);
3024ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                }
3025ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            });
302690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            assertTrue("target position should be checked on time ",
302790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    targetCheck.await(10, TimeUnit.SECONDS));
3028ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            checkForMainThreadException();
3029ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertTrue("on stop should be called", calledOnStop.await(30, TimeUnit.SECONDS));
3030ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            checkForMainThreadException();
3031ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertNotNull("should scroll to new target " + newTarget
3032115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    , rv.findViewHolderForLayoutPosition(newTarget));
3033ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            if (DEBUG) {
3034ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                Log.d(TAG, "on stop has been called on time");
3035ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            }
3036ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        } else {
3037ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertTrue("on stop should be called eventually",
3038ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar                    calledOnStop.await(30, TimeUnit.SECONDS));
3039ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar            assertNotNull("scroll to position should succeed",
3040115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    rv.findViewHolderForLayoutPosition(targetPosition));
3041ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        }
3042ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar        checkForMainThreadException();
3043ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar    }
3044ee30f03253207f694cc46063b0c8c7cb91d24a6eYigit Boyar
3045be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
30467b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    public void smoothScrollToPosition_targetNotFoundSeekInXAndY_scrollsLayoutManagerBy1InXAndY()
30477b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            throws Throwable {
30487b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        smoothScrollToPosition_initialScroll(
30497b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                15,
30507b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                10,
30517b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                9432,
30527b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1239,
30537b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1,
30547b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1,
30557b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                2);
30567b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    }
30577b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
30587b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    @Test
30597b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    public void smoothScrollToPosition_targetNotFoundSeekNegative_scrollsLayoutManagerByMinus1()
30607b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            throws Throwable {
30617b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        smoothScrollToPosition_initialScroll(
30627b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                15,
30637b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                10,
30647b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                -9432,
30657b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                -1239,
30667b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                -1,
30677b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                -1,
30687b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                2);
30697b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    }
30707b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
30717b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    @Test
30727b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    public void smoothScrollToPosition_targetNotFoundSeekInX_scrollsLayoutManagerBy1InX()
30737b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            throws Throwable {
30747b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        smoothScrollToPosition_initialScroll(
30757b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                15,
30767b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                10,
30777b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                0,
30787b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1239,
30797b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                0,
30807b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1,
30817b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1);
30827b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    }
30837b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
30847b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    @Test
30857b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    public void smoothScrollToPosition_targetNotFoundSeekInY_scrollsLayoutManagerBy1InY()
30867b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            throws Throwable {
30877b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        smoothScrollToPosition_initialScroll(
30887b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                15,
30897b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                10,
30907b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                0,
30917b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1239,
30927b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                0,
30937b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1,
30947b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1);
30957b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    }
30967b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
30977b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    @Test
30987b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    public void smoothScrollToPosition_targetFound_doesNotScrollLayoutManager()
30997b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            throws Throwable {
31007b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        smoothScrollToPosition_initialScroll(
31017b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                5,
31027b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                10,
31037b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                9432,
31047b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1239,
31057b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                0,
31067b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                0,
31077b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                1);
31087b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    }
31097b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31107b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    @SuppressWarnings("SameParameterValue")
31117b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    private void smoothScrollToPosition_initialScroll(
31127b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            final int targetItemPosition,
31137b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            final int itemLayoutCount,
31147b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            final int dxIncrement,
31157b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            final int dyIncrement,
31167b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            final int expectedInitialScrollDx,
31177b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            final int expectedInitialScrollDy,
31187b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            final int eventCount)
31197b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            throws Throwable {
31207b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        final RecyclerView rv = new RecyclerView(getActivity());
31217b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        rv.setLayoutParams(
31227b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                new ViewGroup.LayoutParams(
31237b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                        ViewGroup.LayoutParams.MATCH_PARENT,
31247b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                        ViewGroup.LayoutParams.MATCH_PARENT));
31257b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31267b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        TestAdapter testAdapter = new TestAdapter(itemLayoutCount * 2);
31277b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        rv.setAdapter(testAdapter);
31287b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
3129c4df063cbb243aa87d4e3e99a626bde6a7710cf1shepshapard        final CountDownLatch countDownLatch = new CountDownLatch(eventCount);
31307b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        final AtomicInteger actualInitialScrollDx = new AtomicInteger(0);
31317b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        final AtomicInteger actualInitialScrollDy = new AtomicInteger(0);
31327b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31337b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        TestLayoutManager testLayoutManager = new TestLayoutManager() {
31347b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            @Override
31357b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
31367b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    int position) {
31377b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                RecyclerView.SmoothScroller scroller = new RecyclerView.SmoothScroller() {
31387b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    @Override
31397b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    protected void onStart() {
31407b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31417b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    }
31427b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31437b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    @Override
31447b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    protected void onStop() {
31457b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31467b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    }
31477b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31487b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    @Override
31497b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state,
31507b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                            Action action) {
31517b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    }
31527b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31537b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    @Override
31547b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    protected void onTargetFound(View targetView, RecyclerView.State state,
31557b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                            Action action) {
31567b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                        countDownLatch.countDown();
31577b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    }
31587b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31597b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    @Nullable
31607b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    @Override
31617b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    public PointF computeScrollVectorForPosition(int targetPosition) {
31627b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                        return new PointF(dxIncrement, dyIncrement);
31637b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    }
31647b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                };
31657b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                scroller.setTargetPosition(position);
31667b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                startSmoothScroll(scroller);
31677b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            }
31687b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31697b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            @Override
31707b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
31717b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                layoutRange(recycler, 0, itemLayoutCount);
31727b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            }
31737b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31747b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            @Override
31757b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
31767b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    RecyclerView.State state) {
31777b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                actualInitialScrollDx.set(dx);
31787b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                countDownLatch.countDown();
31797b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                return 0;
31807b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            }
31817b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31827b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            @Override
31837b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
31847b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                    RecyclerView.State state) {
31857b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                actualInitialScrollDy.set(dy);
31867b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                countDownLatch.countDown();
31877b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                return 0;
31887b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            }
31897b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        };
31907b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31917b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        rv.setLayoutManager(testLayoutManager);
31927b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31937b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        getActivity().getContainer().expectLayouts(1);
31947b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        setRecyclerView(rv);
31957b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        getActivity().getContainer().waitForLayout(2);
31967b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
31977b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        mActivityRule.runOnUiThread(new Runnable() {
31987b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            @Override
31997b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            public void run() {
32007b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard                rv.smoothScrollToPosition(targetItemPosition);
32017b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard            }
32027b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        });
32037b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        assertTrue(countDownLatch.await(2, TimeUnit.SECONDS));
32047b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
32057b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        assertThat(actualInitialScrollDx.get(), equalTo(expectedInitialScrollDx));
32067b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard        assertThat(actualInitialScrollDy.get(), equalTo(expectedInitialScrollDy));
32077b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    }
32087b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard
32097b926c6cb92ecc663439b200559d3c0fcfa86980shepshapard    @Test
32100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void consecutiveSmoothScroll() throws Throwable {
321178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final AtomicInteger visibleChildCount = new AtomicInteger(10);
321278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final AtomicInteger totalScrolled = new AtomicInteger(0);
321378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
321478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            int start = 0;
321578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
321678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
321778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
321878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                super.onLayoutChildren(recycler, state);
321978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                layoutRange(recycler, start, visibleChildCount.get());
322078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                layoutLatch.countDown();
322178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
322278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
322378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
322478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
322578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                    RecyclerView.State state) {
322678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                totalScrolled.set(totalScrolled.get() + dy);
322778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                return dy;
322878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
322978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
323078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
323178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public boolean canScrollVertically() {
323278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                return true;
323378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
323478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        };
323578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
323678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        TestAdapter testAdapter = new TestAdapter(500);
323778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        rv.setLayoutManager(lm);
323878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        rv.setAdapter(testAdapter);
323978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        lm.expectLayouts(1);
324078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        setRecyclerView(rv);
324178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        lm.waitForLayout(1);
324242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
324378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
324478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public void run() {
324578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                rv.smoothScrollBy(0, 2000);
324678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
324778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        });
324878f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        Thread.sleep(250);
324978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final AtomicInteger scrollAmt = new AtomicInteger();
325042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
325178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            @Override
325278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            public void run() {
325378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                final int soFar = totalScrolled.get();
325478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                scrollAmt.set(soFar);
325578f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar                rv.smoothScrollBy(0, 5000 - soFar);
325678f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            }
325778f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        });
32581a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        while (rv.getScrollState() != SCROLL_STATE_IDLE) {
325978f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar            Thread.sleep(100);
326078f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        }
326178f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        final int soFar = totalScrolled.get();
326278f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar        assertEquals("second scroll should be competed properly", 5000, soFar);
326378f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar    }
326478f659a66b021d5d0e36800a7144e956d8054ceaYigit Boyar
32655cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar    public void accessRecyclerOnOnMeasureTest(final boolean enablePredictiveAnimations)
32665cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            throws Throwable {
32675cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
32685cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        final AtomicInteger expectedOnMeasureStateCount = new AtomicInteger(10);
32695cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
32705cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            @Override
32715cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
32725cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                super.onLayoutChildren(recycler, state);
32735cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                try {
32745cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
32755cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    layoutLatch.countDown();
32765cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                } catch (Throwable t) {
32775cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    postExceptionToInstrumentation(t);
32785cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                } finally {
32795cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    layoutLatch.countDown();
32805cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                }
32815cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            }
32825cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
32835cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            @Override
32845cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
32855cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    int widthSpec, int heightSpec) {
32865cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                try {
32875cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    // make sure we access all views
32885cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    for (int i = 0; i < state.getItemCount(); i++) {
32895cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        View view = recycler.getViewForPosition(i);
32905cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        assertNotNull(view);
32915cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        assertEquals(i, getPosition(view));
32925cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    }
32934143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    if (!state.isPreLayout()) {
32944143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        assertEquals(state.toString(),
32954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                                expectedOnMeasureStateCount.get(), state.getItemCount());
32964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    }
3297824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                } catch (Throwable t) {
32985cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                    postExceptionToInstrumentation(t);
32995cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                }
33005cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                super.onMeasure(recycler, state, widthSpec, heightSpec);
33015cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            }
33025cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
33035cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            @Override
33045cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            public boolean supportsPredictiveItemAnimations() {
33055cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                return enablePredictiveAnimations;
33065cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            }
33075cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        };
33085cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
33095cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        recyclerView.setLayoutManager(lm);
33105cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        recyclerView.setAdapter(testAdapter);
33115cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        recyclerView.setLayoutManager(lm);
33125cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.expectLayouts(1);
33135cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        setRecyclerView(recyclerView);
33145cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.waitForLayout(2);
33155cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        checkForMainThreadException();
33165cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.expectLayouts(1);
33175cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        if (!enablePredictiveAnimations) {
33185cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar            expectedOnMeasureStateCount.set(15);
33195cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        }
33205cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        testAdapter.addAndNotify(4, 5);
33215cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        lm.waitForLayout(2);
33225cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar        checkForMainThreadException();
33235cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar    }
33245cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar
3325be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
33260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void setCompatibleAdapter() throws Throwable {
33270b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(true, true);
33280b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
33290b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(false, true);
33300b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
33310b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(true, false);
33320b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
33330b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        compatibleAdapterTest(false, false);
33340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        removeRecyclerView();
33350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
33360b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
33370b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    private void compatibleAdapterTest(boolean useCustomPool, boolean removeAndRecycleExistingViews)
33380b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            throws Throwable {
33390b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
33400b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        final AtomicInteger recycledViewCount = new AtomicInteger();
33410b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
33420b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
33430b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
33440b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                try {
33450b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
33460b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
33470b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } catch (Throwable t) {
33480b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    postExceptionToInstrumentation(t);
33490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } finally {
33500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
33510b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                }
33520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
33530b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        };
33540b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
33550b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setLayoutManager(lm);
33560b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setAdapter(testAdapter);
33570b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setRecyclerListener(new RecyclerView.RecyclerListener() {
33580b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
33598a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
33600b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                recycledViewCount.incrementAndGet();
33610b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
33620b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        });
33630b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
33640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        setRecyclerView(recyclerView, !useCustomPool);
33650b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
33660b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
33670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
33680b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        swapAdapter(new TestAdapter(10), removeAndRecycleExistingViews);
33690b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
33700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
33710b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        if (removeAndRecycleExistingViews) {
33720b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            assertTrue("Previous views should be recycled", recycledViewCount.get() > 0);
33730b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        } else {
33740b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            assertEquals("No views should be recycled if adapters are compatible and developer "
33750b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    + "did not request a recycle", 0, recycledViewCount.get());
33760b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
33770b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
33780b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
3379be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
33800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void setIncompatibleAdapter() throws Throwable {
33810b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        incompatibleAdapterTest(true);
33820b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        incompatibleAdapterTest(false);
33830b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
33840b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
33850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    public void incompatibleAdapterTest(boolean useCustomPool) throws Throwable {
33860b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
33870b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
33880b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
33890b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
33900b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                super.onLayoutChildren(recycler, state);
33910b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                try {
33920b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
33930b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
33940b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } catch (Throwable t) {
33950b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    postExceptionToInstrumentation(t);
33960b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                } finally {
33970b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    layoutLatch.countDown();
33980b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                }
33990b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
34000b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        };
34010b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
34020b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setLayoutManager(lm);
34030b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setAdapter(testAdapter);
34040b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        recyclerView.setLayoutManager(lm);
34050b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
34060b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        setRecyclerView(recyclerView, !useCustomPool);
34070b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
34080b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
34090b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.expectLayouts(1);
34100b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        setAdapter(new TestAdapter2(10));
34110b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        lm.waitForLayout(2);
34120b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        checkForMainThreadException();
34130b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
34140b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
3415be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
34160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleIgnored() throws Throwable {
34176e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        final TestAdapter adapter = new TestAdapter(10);
34186e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
34196e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            @Override
34206e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
34216e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                layoutRange(recycler, 0, 5);
34226e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                layoutLatch.countDown();
34236e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            }
34246e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        };
34256e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
34266e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        recyclerView.setAdapter(adapter);
34276e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        recyclerView.setLayoutManager(lm);
34286e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        lm.expectLayouts(1);
34296e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        setRecyclerView(recyclerView);
34306e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        lm.waitForLayout(2);
343142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
34326e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            @Override
34336e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            public void run() {
34346e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                View child1 = lm.findViewByPosition(0);
34356e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                View child2 = lm.findViewByPosition(1);
34366e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.ignoreView(child1);
34376e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.ignoreView(child2);
34386e560171b774afafd572b1896becac7b308a92e6Yigit Boyar
34396e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.removeAndRecycleAllViews(recyclerView.mRecycler);
34406e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                assertEquals("ignored child should not be recycled or removed", 2,
34416e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                        lm.getChildCount());
34426e560171b774afafd572b1896becac7b308a92e6Yigit Boyar
34436e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                Throwable[] throwables = new Throwable[1];
34446e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                try {
34456e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                    lm.removeAndRecycleView(child1, mRecyclerView.mRecycler);
34466e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                } catch (Throwable t) {
34476e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                    throwables[0] = t;
34486e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                }
34496e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                assertTrue("Trying to recycle an ignored view should throw IllegalArgException "
34505cfed5428d421f179f83382ad72ec6edd11a066dYigit Boyar                        , throwables[0] instanceof IllegalArgumentException);
34516e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                lm.removeAllViews();
34526e560171b774afafd572b1896becac7b308a92e6Yigit Boyar                assertEquals("ignored child should be removed as well ", 0, lm.getChildCount());
34536e560171b774afafd572b1896becac7b308a92e6Yigit Boyar            }
34546e560171b774afafd572b1896becac7b308a92e6Yigit Boyar        });
34556e560171b774afafd572b1896becac7b308a92e6Yigit Boyar    }
34566e560171b774afafd572b1896becac7b308a92e6Yigit Boyar
3457be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
34580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void findIgnoredByPosition() throws Throwable {
3459f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final TestAdapter adapter = new TestAdapter(10);
3460f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
3461f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            @Override
3462f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3463f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                detachAndScrapAttachedViews(recycler);
3464f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                layoutRange(recycler, 0, 5);
3465f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                layoutLatch.countDown();
3466f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            }
3467f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        };
3468f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
3469f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        recyclerView.setAdapter(adapter);
3470f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        recyclerView.setLayoutManager(lm);
3471f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.expectLayouts(1);
3472f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        setRecyclerView(recyclerView);
3473f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.waitForLayout(2);
3474f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        Thread.sleep(5000);
3475f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final int pos = 1;
3476f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        final View[] ignored = new View[1];
347742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
3478f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            @Override
3479f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            public void run() {
3480f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                View child = lm.findViewByPosition(pos);
3481f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                lm.ignoreView(child);
3482f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                ignored[0] = child;
3483f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar            }
3484f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        });
3485f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNotNull("ignored child should not be null", ignored[0]);
3486f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNull("find view by position should not return ignored child",
3487f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar                lm.findViewByPosition(pos));
3488f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.expectLayouts(1);
3489f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        requestLayoutOnUIThread(mRecyclerView);
3490f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        lm.waitForLayout(1);
3491f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertEquals("child count should be ", 6, lm.getChildCount());
3492f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        View replacement = lm.findViewByPosition(pos);
3493f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNotNull("re-layout should replace ignored child w/ another one", replacement);
3494f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar        assertNotSame("replacement should be a different view", replacement, ignored[0]);
3495f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar    }
3496f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar
3497be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
3498178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar    public void itemDecorsWithPredictive() throws Throwable {
3499178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        LayoutAllLayoutManager lm = new LayoutAllLayoutManager(true);
3500178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        lm.setSupportsPredictive(true);
3501178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        final Object changePayload = new Object();
3502178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        final TestAdapter adapter = new TestAdapter(10) {
3503178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            @Override
3504178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
3505178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    int position, List<Object> payloads) {
3506178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                super.onBindViewHolder(holder, position);
3507178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                holder.setData(payloads.isEmpty() ? null : payloads.get(0));
3508178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            }
3509178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        };
3510178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        final Map<Integer, Object> preLayoutData = new HashMap<>();
3511178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        final Map<Integer, Object> postLayoutData = new HashMap<>();
3512178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar
3513178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        final RecyclerView.ItemDecoration decoration = new RecyclerView.ItemDecoration() {
3514178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            @Override
3515178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
3516178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    RecyclerView.State state) {
3517178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                try {
3518178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    TestViewHolder tvh = (TestViewHolder) parent.getChildViewHolder(view);
3519178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    Object data = tvh.getData();
3520178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    int adapterPos = tvh.getAdapterPosition();
3521178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    assertThat(adapterPos, is(not(NO_POSITION)));
3522178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    if (state.isPreLayout()) {
3523178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                        preLayoutData.put(adapterPos, data);
3524178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    } else {
3525178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                        postLayoutData.put(adapterPos, data);
3526178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    }
3527178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                } catch (Throwable t) {
3528178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                    postExceptionToInstrumentation(t);
3529178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                }
3530178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar
3531178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            }
3532178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        };
3533178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
3534178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        rv.addItemDecoration(decoration);
3535178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        rv.setAdapter(adapter);
3536178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        rv.setLayoutManager(lm);
3537178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        lm.expectLayouts(1);
3538178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        setRecyclerView(rv);
3539178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        lm.waitForLayout(2);
3540178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar
3541178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        preLayoutData.clear();
3542178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        postLayoutData.clear();
3543178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        lm.expectLayouts(2);
354442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
3545178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            @Override
3546178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            public void run() {
3547178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar                adapter.notifyItemChanged(3, changePayload);
3548178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar            }
3549178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        });
3550178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        lm.waitForLayout(2);
3551178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        assertThat(preLayoutData.containsKey(3), is(false));
3552178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        assertThat(postLayoutData.get(3), is(changePayload));
3553178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        assertThat(preLayoutData.size(), is(0));
3554178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        assertThat(postLayoutData.size(), is(1));
3555178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar        checkForMainThreadException();
3556178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar    }
3557178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar
3558178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar    @Test
35590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void invalidateAllDecorOffsets() throws Throwable {
3560bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final TestAdapter adapter = new TestAdapter(10);
3561bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
3562bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final AtomicBoolean invalidatedOffsets = new AtomicBoolean(true);
3563bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        recyclerView.setAdapter(adapter);
3564bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final AtomicInteger layoutCount = new AtomicInteger(4);
3565bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        final RecyclerView.ItemDecoration dummyItemDecoration = new RecyclerView.ItemDecoration() {
3566bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        };
3567bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        TestLayoutManager testLayoutManager = new TestLayoutManager() {
3568bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
3569bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3570bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                try {
3571bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    // test
3572824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    for (int i = 0; i < getChildCount(); i++) {
3573bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        View child = getChildAt(i);
3574bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)
3575bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                child.getLayoutParams();
3576bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        assertEquals(
3577bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                "Decor insets validation for VH should have expected value.",
3578bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                invalidatedOffsets.get(), lp.mInsetsDirty);
3579bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    }
3580bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    for (RecyclerView.ViewHolder vh : mRecyclerView.mRecycler.mCachedViews) {
3581bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)
3582bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                vh.itemView.getLayoutParams();
3583bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                        assertEquals(
3584bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                "Decor insets invalidation in cache for VH should have expected "
3585bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                        + "value.",
3586bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                                invalidatedOffsets.get(), lp.mInsetsDirty);
3587bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    }
3588bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    detachAndScrapAttachedViews(recycler);
3589bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    layoutRange(recycler, 0, layoutCount.get());
3590bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                } catch (Throwable t) {
3591bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    postExceptionToInstrumentation(t);
3592bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                } finally {
3593bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                    layoutLatch.countDown();
3594bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                }
3595bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
3596bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3597bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
3598bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
3599bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                return false;
3600bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
3601bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        };
3602bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // first layout
3603bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        recyclerView.setItemViewCacheSize(5);
3604bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        recyclerView.setLayoutManager(testLayoutManager);
3605bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3606e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        setRecyclerView(recyclerView, true, false);
3607bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(2);
3608bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3609bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3610bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // re-layout w/o any change
3611bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(false);
3612bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3613bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        requestLayoutOnUIThread(recyclerView);
3614bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
3615bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3616bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3617bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // invalidate w/o an item decorator
3618e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar
3619bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
3620bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3621bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
3622bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.assertNoLayout("layout should not happen", 2);
3623bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3624bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3625bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // set item decorator, should invalidate
3626bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
3627bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3628bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        addItemDecoration(mRecyclerView, dummyItemDecoration);
3629bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
3630bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3631bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3632bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // re-layout w/o any change
3633bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(false);
3634bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3635bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        requestLayoutOnUIThread(recyclerView);
3636bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
3637bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3638bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3639bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // invalidate w/ item decorator
3640bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
3641bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
3642bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3643bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
3644bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(2);
3645bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3646bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3647bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // trigger cache.
3648bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        layoutCount.set(3);
3649bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(false);
3650bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3651bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        requestLayoutOnUIThread(mRecyclerView);
3652bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
3653bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3654bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        assertEquals("a view should be cached", 1, mRecyclerView.mRecycler.mCachedViews.size());
3655bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3656bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        layoutCount.set(5);
3657bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
3658bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3659bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidateDecorOffsets(recyclerView);
3660bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
3661bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3662bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3663bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        // remove item decorator
3664bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        invalidatedOffsets.set(true);
3665bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.expectLayouts(1);
3666bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        removeItemDecoration(mRecyclerView, dummyItemDecoration);
3667bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        testLayoutManager.waitForLayout(1);
3668bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        checkForMainThreadException();
3669bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
3670bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3671bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    public void addItemDecoration(final RecyclerView recyclerView, final
3672824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    RecyclerView.ItemDecoration itemDecoration) throws Throwable {
367342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
3674bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
3675bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void run() {
3676bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                recyclerView.addItemDecoration(itemDecoration);
3677bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
3678bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        });
3679bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
3680bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3681bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    public void removeItemDecoration(final RecyclerView recyclerView, final
3682bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    RecyclerView.ItemDecoration itemDecoration) throws Throwable {
368342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
3684bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
3685bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void run() {
3686bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                recyclerView.removeItemDecoration(itemDecoration);
3687bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
3688bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        });
3689bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
3690bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3691bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    public void invalidateDecorOffsets(final RecyclerView recyclerView) throws Throwable {
369242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
3693bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            @Override
3694bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            public void run() {
3695bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar                recyclerView.invalidateItemDecorations();
3696bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar            }
3697bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar        });
3698bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar    }
3699bc2c6d6432a227d46a43fd18eb204d89ca472064Yigit Boyar
3700be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
37010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void invalidateDecorOffsets() throws Throwable {
3702cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        final TestAdapter adapter = new TestAdapter(10);
3703cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.setHasStableIds(true);
3704cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        final RecyclerView recyclerView = new RecyclerView(getActivity());
3705cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        recyclerView.setAdapter(adapter);
3706cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
370732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        final Map<Long, Boolean> changes = new HashMap<>();
3708cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
3709cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        TestLayoutManager testLayoutManager = new TestLayoutManager() {
3710cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            @Override
3711cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3712cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                try {
3713cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    if (changes.size() > 0) {
3714cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                        // test
3715824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                        for (int i = 0; i < getChildCount(); i++) {
3716cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            View child = getChildAt(i);
3717cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)
3718cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                                    child.getLayoutParams();
3719cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            RecyclerView.ViewHolder vh = lp.mViewHolder;
3720cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            if (!changes.containsKey(vh.getItemId())) {
3721cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                                continue; //nothing to test
3722cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            }
3723cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                            assertEquals(
372432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                                    "Decor insets validation for VH should have expected value.",
372532b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar                                    changes.get(vh.getItemId()), lp.mInsetsDirty);
3726cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                        }
3727cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    }
3728cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    detachAndScrapAttachedViews(recycler);
3729cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    layoutRange(recycler, 0, state.getItemCount());
3730cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                } catch (Throwable t) {
3731cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    postExceptionToInstrumentation(t);
3732cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                } finally {
3733cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    layoutLatch.countDown();
3734cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                }
3735cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            }
3736cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
3737cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            @Override
3738cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            public boolean supportsPredictiveItemAnimations() {
3739cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                return false;
3740cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            }
3741cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        };
3742cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        recyclerView.setLayoutManager(testLayoutManager);
3743cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
3744cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        setRecyclerView(recyclerView);
3745cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
3746cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        int itemAddedTo = 5;
3747cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        for (int i = 0; i < itemAddedTo; i++) {
3748115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), false);
3749cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
3750cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        for (int i = itemAddedTo; i < mRecyclerView.getChildCount(); i++) {
3751115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), true);
3752cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
3753cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
3754cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.addAndNotify(5, 1);
3755cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
3756cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        checkForMainThreadException();
3757cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
3758cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        changes.clear();
3759cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        int[] changedItems = new int[]{3, 5, 6};
3760824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        for (int i = 0; i < adapter.getItemCount(); i++) {
3761115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), false);
3762cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
376332b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        for (int changedItem : changedItems) {
376432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(changedItem).getItemId(),
37651a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    true);
3766cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
3767cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
3768cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.changePositionsAndNotify(changedItems);
3769cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
3770cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        checkForMainThreadException();
3771cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
3772824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        for (int i = 0; i < adapter.getItemCount(); i++) {
3773115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            changes.put(mRecyclerView.findViewHolderForLayoutPosition(i).getItemId(), true);
3774cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
3775cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.expectLayouts(1);
3776cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.dispatchDataSetChanged();
3777cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        testLayoutManager.waitForLayout(2);
3778cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        checkForMainThreadException();
3779cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase    }
3780cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
3781be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
37820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void movingViaStableIds() throws Throwable {
3783c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        stableIdsMoveTest(true);
3784c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        removeRecyclerView();
3785c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        stableIdsMoveTest(false);
3786c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        removeRecyclerView();
3787c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar    }
3788c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
3789c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar    public void stableIdsMoveTest(final boolean supportsPredictive) throws Throwable {
3790c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
3791c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        testAdapter.setHasStableIds(true);
3792c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final AtomicBoolean test = new AtomicBoolean(false);
3793c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final int movedViewFromIndex = 3;
3794c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final int movedViewToIndex = 6;
3795c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        final View[] movedView = new View[1];
3796c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
3797c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            @Override
3798c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
3799c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                detachAndScrapAttachedViews(recycler);
3800c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                try {
3801c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    if (test.get()) {
3802c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                        if (state.isPreLayout()) {
3803c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            View view = recycler.getViewForPosition(movedViewFromIndex, true);
3804c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertSame("In pre layout, should be able to get moved view w/ old "
3805c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                                    + "position", movedView[0], view);
3806c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);
3807c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertTrue("it should come from scrap", holder.wasReturnedFromScrap());
3808c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            // clear scrap flag
3809c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            holder.clearReturnedFromScrapFlag();
3810c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                        } else {
3811c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            View view = recycler.getViewForPosition(movedViewToIndex, true);
3812c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertSame("In post layout, should be able to get moved view w/ new "
3813c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                                    + "position", movedView[0], view);
3814c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);
3815c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            assertTrue("it should come from scrap", holder.wasReturnedFromScrap());
3816c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            // clear scrap flag
3817c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            holder.clearReturnedFromScrapFlag();
3818c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                        }
3819c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    }
3820c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
3821c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                } catch (Throwable t) {
3822c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    postExceptionToInstrumentation(t);
3823c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                } finally {
3824c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    layoutLatch.countDown();
3825c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                }
3826c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
3827c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
3828c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            }
3829c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
3830c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            @Override
3831c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            public boolean supportsPredictiveItemAnimations() {
3832c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                return supportsPredictive;
3833c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            }
3834c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        };
3835c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        RecyclerView recyclerView = new RecyclerView(this.getActivity());
3836c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        recyclerView.setAdapter(testAdapter);
3837c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        recyclerView.setLayoutManager(lm);
3838c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.expectLayouts(1);
3839c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        setRecyclerView(recyclerView);
3840c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.waitForLayout(1);
3841c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
3842c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        movedView[0] = recyclerView.getChildAt(movedViewFromIndex);
3843c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        test.set(true);
3844c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.expectLayouts(supportsPredictive ? 2 : 1);
384542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
3846c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            @Override
3847c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            public void run() {
3848c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                Item item = testAdapter.mItems.remove(movedViewFromIndex);
3849c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                testAdapter.mItems.add(movedViewToIndex, item);
3850c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                testAdapter.notifyItemRemoved(movedViewFromIndex);
3851c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                testAdapter.notifyItemInserted(movedViewToIndex);
3852c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            }
3853c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        });
3854c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        lm.waitForLayout(2);
3855c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        checkForMainThreadException();
3856c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar    }
3857cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
3858be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
38590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterChangeDuringLayout() throws Throwable {
38600bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyDataSetChanged", new Runnable() {
38610bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
38620bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
38630bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyDataSetChanged();
38640bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
38650bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
38660bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
38670bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyItemChanged", new Runnable() {
38680bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
38690bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
38700bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyItemChanged(2);
38710bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
38720bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
38730bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
38740bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyItemInserted", new Runnable() {
38750bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
38760bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
38770bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyItemInserted(2);
38780bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
38790bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
38800bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        adapterChangeInMainThreadTest("notifyItemRemoved", new Runnable() {
38810bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
38820bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void run() {
38830bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                mRecyclerView.getAdapter().notifyItemRemoved(2);
38840bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
38850bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        });
38860bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
38870bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
38880bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    public void adapterChangeInMainThreadTest(String msg,
38890bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            final Runnable onLayoutRunnable) throws Throwable {
389066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        setIgnoreMainThreadException(true);
38910bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        final AtomicBoolean doneFirstLayout = new AtomicBoolean(false);
38920bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestAdapter testAdapter = new TestAdapter(10);
38930bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
38940bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
38950bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
38960bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                super.onLayoutChildren(recycler, state);
38970bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                try {
38980bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
38990bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    if (doneFirstLayout.get()) {
39000bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        onLayoutRunnable.run();
39010bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    }
39020bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } catch (Throwable t) {
39030bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    postExceptionToInstrumentation(t);
39040bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } finally {
39050bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutLatch.countDown();
39060bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
39070bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
39080bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
39090bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        };
39100bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
39110bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setLayoutManager(lm);
39120bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setAdapter(testAdapter);
39130bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
39140bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        setRecyclerView(recyclerView);
39150bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
39160bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        doneFirstLayout.set(true);
39170bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
39180bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        requestLayoutOnUIThread(recyclerView);
39190bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
39200bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        removeRecyclerView();
39210bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        assertTrue("Invalid data updates should be caught:" + msg,
392266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                getMainThreadException() instanceof IllegalStateException);
39230bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
39240bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
3925be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
39260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterChangeDuringScroll() throws Throwable {
39270bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        for (int orientation : new int[]{OrientationHelper.HORIZONTAL,
39280bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                OrientationHelper.VERTICAL}) {
39290bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyDataSetChanged", orientation,
39300bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    new Runnable() {
39310bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        @Override
39320bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        public void run() {
39330bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                            mRecyclerView.getAdapter().notifyDataSetChanged();
39340bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        }
39350bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    });
39360bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyItemChanged", orientation, new Runnable() {
39370bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                @Override
39380bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                public void run() {
39390bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    mRecyclerView.getAdapter().notifyItemChanged(2);
39400bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
39410bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            });
39420bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
39430bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyItemInserted", orientation, new Runnable() {
39440bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                @Override
39450bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                public void run() {
39460bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    mRecyclerView.getAdapter().notifyItemInserted(2);
39470bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
39480bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            });
39490bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            adapterChangeDuringScrollTest("notifyItemRemoved", orientation, new Runnable() {
39500bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                @Override
39510bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                public void run() {
39520bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    mRecyclerView.getAdapter().notifyItemRemoved(2);
39530bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
39540bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            });
39550bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        }
39560bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
39570bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
39580bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    public void adapterChangeDuringScrollTest(String msg, final int orientation,
39590bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            final Runnable onScrollRunnable) throws Throwable {
396066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        setIgnoreMainThreadException(true);
39610bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestAdapter testAdapter = new TestAdapter(100);
39620bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
39630bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
39640bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
39650bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                super.onLayoutChildren(recycler, state);
39660bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                try {
39670bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutRange(recycler, 0, 10);
39680bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } catch (Throwable t) {
39690bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    postExceptionToInstrumentation(t);
39700bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } finally {
39710bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutLatch.countDown();
39720bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
39730bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
39740bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
39750bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
39760bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public boolean canScrollVertically() {
39770bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return orientation == OrientationHelper.VERTICAL;
39780bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
39790bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
39800bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
39810bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public boolean canScrollHorizontally() {
39820bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return orientation == OrientationHelper.HORIZONTAL;
39830bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
39840bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
39850bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public int mockScroll() {
39860bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                try {
39870bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    onScrollRunnable.run();
39880bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } catch (Throwable t) {
39890bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    postExceptionToInstrumentation(t);
39900bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                } finally {
39910bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    layoutLatch.countDown();
39920bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                }
39930bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return 0;
39940bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
39950bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
39960bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
39970bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
39980bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    RecyclerView.State state) {
39990bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return mockScroll();
40000bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
40010bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
40020bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            @Override
40030bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
40040bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                    RecyclerView.State state) {
40050bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                return mockScroll();
40060bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar            }
40070bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        };
40080bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        RecyclerView recyclerView = new RecyclerView(getActivity());
40090bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setLayoutManager(lm);
40100bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        recyclerView.setAdapter(testAdapter);
40110bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
40120bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        setRecyclerView(recyclerView);
40130bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
40140bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.expectLayouts(1);
40150bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        scrollBy(200);
40160bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        lm.waitForLayout(2);
40170bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        removeRecyclerView();
40180bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar        assertTrue("Invalid data updates should be caught:" + msg,
401966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar                getMainThreadException() instanceof IllegalStateException);
40200bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar    }
40210bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
4022be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
40230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleOnDetach() throws Throwable {
402449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
402549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final TestAdapter testAdapter = new TestAdapter(10);
402649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final AtomicBoolean didRunOnDetach = new AtomicBoolean(false);
402749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
402849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
402949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
403049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                super.onLayoutChildren(recycler, state);
403149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                layoutRange(recycler, 0, state.getItemCount() - 1);
403249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                layoutLatch.countDown();
403349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
403449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
403549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
403649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
403749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                super.onDetachedFromWindow(view, recycler);
403849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                didRunOnDetach.set(true);
403949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                removeAndRecycleAllViews(recycler);
404049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
404149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        };
404249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        recyclerView.setAdapter(testAdapter);
404349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        recyclerView.setLayoutManager(lm);
404449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        lm.expectLayouts(1);
404549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setRecyclerView(recyclerView);
404649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        lm.waitForLayout(2);
404749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        removeRecyclerView();
404849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        assertTrue("When recycler view is removed, detach should run", didRunOnDetach.get());
404949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        assertEquals("All children should be recycled", recyclerView.getChildCount(), 0);
405049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
40510bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar
4052be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
40530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void updatesWhileDetached() throws Throwable {
4054bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
4055bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final int initialAdapterSize = 20;
4056bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final TestAdapter adapter = new TestAdapter(initialAdapterSize);
4057bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final AtomicInteger layoutCount = new AtomicInteger(0);
4058bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
4059bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            @Override
4060bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
4061bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                super.onLayoutChildren(recycler, state);
4062bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutRange(recycler, 0, 5);
4063bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutCount.incrementAndGet();
4064bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutLatch.countDown();
4065bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            }
4066bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        };
4067bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setAdapter(adapter);
4068bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setLayoutManager(lm);
4069bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setHasFixedSize(true);
4070bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.expectLayouts(1);
4071bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        adapter.addAndNotify(4, 5);
4072bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.assertNoLayout("When RV is not attached, layout should not happen", 1);
4073bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar    }
4074bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar
4075be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
40760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void updatesAfterDetach() throws Throwable {
4077bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
4078bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final int initialAdapterSize = 20;
4079bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final TestAdapter adapter = new TestAdapter(initialAdapterSize);
4080bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final AtomicInteger layoutCount = new AtomicInteger(0);
4081bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
4082bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            @Override
4083bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
4084bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                super.onLayoutChildren(recycler, state);
4085bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutRange(recycler, 0, 5);
4086bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutCount.incrementAndGet();
4087bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutLatch.countDown();
4088bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            }
4089bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        };
4090bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setAdapter(adapter);
4091bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setLayoutManager(lm);
4092bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.expectLayouts(1);
4093bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        recyclerView.setHasFixedSize(true);
4094bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        setRecyclerView(recyclerView);
4095bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.waitForLayout(2);
4096bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.expectLayouts(1);
4097bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        final int prevLayoutCount = layoutCount.get();
409842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
4099bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            @Override
4100bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            public void run() {
4101bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                try {
4102bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                    adapter.addAndNotify(4, 5);
4103bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                    removeRecyclerView();
4104bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                } catch (Throwable throwable) {
410590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    postExceptionToInstrumentation(throwable);
4106bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                }
4107bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar            }
4108bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        });
410990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        checkForMainThreadException();
4110bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar
4111bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        lm.assertNoLayout("When RV is not attached, layout should not happen", 1);
4112bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar        assertEquals("No extra layout should happen when detached", prevLayoutCount,
4113bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar                layoutCount.get());
4114bbf19899050278ae64da0a8bfce209a9f359c853Yigit Boyar    }
411549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
4116be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
41170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void notifyDataSetChangedWithStableIds() throws Throwable {
411832b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        final Map<Integer, Integer> oldPositionToNewPositionMapping = new HashMap<>();
4119e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final TestAdapter adapter = new TestAdapter(100) {
4120e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
4121e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public long getItemId(int position) {
4122e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                return mItems.get(position).mId;
4123e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
4124e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        };
4125e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        adapter.setHasStableIds(true);
412632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar        final ArrayList<Item> previousItems = new ArrayList<>();
4127e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        previousItems.addAll(adapter.mItems);
4128e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
4129e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final AtomicInteger layoutStart = new AtomicInteger(50);
4130e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final AtomicBoolean validate = new AtomicBoolean(false);
4131e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final int childCount = 10;
4132e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
4133e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
4134e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
4135e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                try {
4136e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    super.onLayoutChildren(recycler, state);
4137e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    if (validate.get()) {
4138e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        assertEquals("Cached views should be kept", 5, recycler
4139e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                .mCachedViews.size());
4140e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        for (RecyclerView.ViewHolder vh : recycler.mCachedViews) {
4141e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            TestViewHolder tvh = (TestViewHolder) vh;
41420bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                            assertTrue("view holder should be marked for update",
41430bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                                    tvh.needsUpdate());
4144e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            assertTrue("view holder should be marked as invalid", tvh.isInvalid());
4145e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        }
4146e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
4147e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    detachAndScrapAttachedViews(recycler);
4148e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    if (validate.get()) {
4149e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        assertEquals("cache size should stay the same", 5,
4150e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                recycler.mCachedViews.size());
4151e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        assertEquals("all views should be scrapped", childCount,
4152e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                recycler.getScrapList().size());
4153e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        for (RecyclerView.ViewHolder vh : recycler.getScrapList()) {
4154e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            // TODO create test case for type change
4155e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            TestViewHolder tvh = (TestViewHolder) vh;
41560bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                            assertTrue("view holder should be marked for update",
41570bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                                    tvh.needsUpdate());
4158e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            assertTrue("view holder should be marked as invalid", tvh.isInvalid());
4159e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        }
4160e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
4161e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    layoutRange(recycler, layoutStart.get(), layoutStart.get() + childCount);
4162e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    if (validate.get()) {
4163e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        for (int i = 0; i < getChildCount(); i++) {
4164e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            View view = getChildAt(i);
4165e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            TestViewHolder tvh = (TestViewHolder) mRecyclerView
4166e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                    .getChildViewHolder(view);
4167115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                            final int oldPos = previousItems.indexOf(tvh.mBoundItem);
4168e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            assertEquals("view holder's position should be correct",
4169e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                                    oldPositionToNewPositionMapping.get(oldPos).intValue(),
4170115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    tvh.getLayoutPosition());
41710bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        }
4172e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
4173e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                } catch (Throwable t) {
4174e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    postExceptionToInstrumentation(t);
4175e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                } finally {
4176e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    layoutLatch.countDown();
4177e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                }
4178e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
4179e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        };
4180e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
4181e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setItemAnimator(null);
4182e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setAdapter(adapter);
4183e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setLayoutManager(lm);
4184e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        recyclerView.setItemViewCacheSize(10);
4185e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.expectLayouts(1);
4186e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        setRecyclerView(recyclerView);
4187e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.waitForLayout(2);
4188e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        checkForMainThreadException();
4189e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        getInstrumentation().waitForIdleSync();
4190e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        layoutStart.set(layoutStart.get() + 5);//55
4191e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.expectLayouts(1);
4192e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        requestLayoutOnUIThread(recyclerView);
4193e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.waitForLayout(2);
4194e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        validate.set(true);
4195e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.expectLayouts(1);
419642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
4197e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
4198e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public void run() {
4199e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                try {
4200e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    adapter.moveItems(false,
4201e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            new int[]{50, 56}, new int[]{51, 1}, new int[]{52, 2},
4202e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            new int[]{53, 54}, new int[]{60, 61}, new int[]{62, 64},
4203e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                            new int[]{75, 58});
4204e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    for (int i = 0; i < previousItems.size(); i++) {
4205e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        Item item = previousItems.get(i);
4206e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        oldPositionToNewPositionMapping.put(i, adapter.mItems.indexOf(item));
4207e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
4208cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    adapter.dispatchDataSetChanged();
4209e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                } catch (Throwable throwable) {
4210e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    postExceptionToInstrumentation(throwable);
4211e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                }
4212e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
4213e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        });
4214e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        lm.waitForLayout(2);
4215e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        checkForMainThreadException();
4216e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
4217e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
4218be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
42190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void callbacksDuringAdapterSwap() throws Throwable {
4220824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        callbacksDuringAdapterChange(true);
4221824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
4222824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4223be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
42240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void callbacksDuringAdapterSet() throws Throwable {
4225824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        callbacksDuringAdapterChange(false);
4226824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
4227824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4228824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    public void callbacksDuringAdapterChange(boolean swap) throws Throwable {
4229824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        final TestAdapter2 adapter1 = swap ? createBinderCheckingAdapter()
4230824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                : createOwnerCheckingAdapter();
4231824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        final TestAdapter2 adapter2 = swap ? createBinderCheckingAdapter()
4232824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                : createOwnerCheckingAdapter();
4233824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4234824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
4235824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
4236824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
4237824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                try {
4238824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
4239824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                } catch (Throwable t) {
4240824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    postExceptionToInstrumentation(t);
4241824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                }
4242824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                layoutLatch.countDown();
4243824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
4244824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        };
4245824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
4246824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        rv.setAdapter(adapter1);
4247824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        rv.setLayoutManager(tlm);
4248824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.expectLayouts(1);
4249824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        setRecyclerView(rv);
4250824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.waitForLayout(1);
4251824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        checkForMainThreadException();
4252824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.expectLayouts(1);
4253824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        if (swap) {
4254824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            swapAdapter(adapter2, true);
4255824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        } else {
4256824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            setAdapter(adapter2);
4257824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        }
4258824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        checkForMainThreadException();
4259824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        tlm.waitForLayout(1);
4260824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        checkForMainThreadException();
4261824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
4262824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4263824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    private TestAdapter2 createOwnerCheckingAdapter() {
4264824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        return new TestAdapter2(10) {
4265824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
42668a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull TestViewHolder2 holder) {
4267824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                assertSame("on recycled should be called w/ the creator adapter", this,
4268824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                        holder.mData);
4269824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onViewRecycled(holder);
4270824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
4271824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4272824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
42738a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder2 holder, int position) {
4274824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onBindViewHolder(holder, position);
4275824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                assertSame("on bind should be called w/ the creator adapter", this, holder.mData);
4276824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
4277824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4278824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
42798a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public TestViewHolder2 onCreateViewHolder(@NonNull ViewGroup parent,
4280824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    int viewType) {
4281824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                final TestViewHolder2 vh = super.onCreateViewHolder(parent, viewType);
4282824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                vh.mData = this;
4283824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                return vh;
4284824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
4285824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        };
4286824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
4287824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4288824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    private TestAdapter2 createBinderCheckingAdapter() {
4289824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        return new TestAdapter2(10) {
4290824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
42918a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onViewRecycled(@NonNull TestViewHolder2 holder) {
4292824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                assertSame("on recycled should be called w/ the creator adapter", this,
4293824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                        holder.mData);
4294824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                holder.mData = null;
4295824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onViewRecycled(holder);
4296824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
4297824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4298824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            @Override
42998a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder2 holder, int position) {
4300824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                super.onBindViewHolder(holder, position);
4301824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                holder.mData = this;
4302824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar            }
4303824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        };
4304824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar    }
4305824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
4306be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
43070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void findViewById() throws Throwable {
43085ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        findViewByIdTest(false);
43095ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        removeRecyclerView();
43105ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        findViewByIdTest(true);
43115ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar    }
43125ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
43135ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar    public void findViewByIdTest(final boolean supportPredictive) throws Throwable {
43145ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
43155ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final int initialAdapterSize = 20;
43165ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final TestAdapter adapter = new TestAdapter(initialAdapterSize);
43175ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final int deleteStart = 6;
43185ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final int deleteCount = 5;
43195ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        recyclerView.setAdapter(adapter);
43205ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        final AtomicBoolean assertPositions = new AtomicBoolean(false);
43215ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        TestLayoutManager lm = new TestLayoutManager() {
43225ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            @Override
43235ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
43245ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                super.onLayoutChildren(recycler, state);
43255ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                if (assertPositions.get()) {
43265ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                    if (state.isPreLayout()) {
43270bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        for (int i = 0; i < deleteStart; i++) {
43285ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i);
43295ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertNotNull("find view by position for existing items should work "
43305ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    + "fine", view);
43315ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertFalse("view should not be marked as removed",
43325ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
43335ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
43345ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
43350bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        for (int i = 0; i < deleteCount; i++) {
43365ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i + deleteStart);
43375ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertNotNull("find view by position should work fine for removed "
43385ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    + "views in pre-layout", view);
43395ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertTrue("view should be marked as removed",
43405ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
43415ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
43425ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
43435ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        for (int i = deleteStart + deleteCount; i < 20; i++) {
43445ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i);
43455ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertNotNull(view);
43465ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertFalse("view should not be marked as removed",
43475ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
43485ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
43495ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
43505ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                    } else {
43510bdfd8728199045676f3ad6c6571e7080099716fYigit Boyar                        for (int i = 0; i < initialAdapterSize - deleteCount; i++) {
43525ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            View view = findViewByPosition(i);
4353fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar                            assertNotNull("find view by position for existing item " + i +
4354fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar                                    " should work fine. child count:" + getChildCount(), view);
43555ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            TestViewHolder viewHolder =
43565ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    (TestViewHolder) mRecyclerView.getChildViewHolder(view);
43575ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertSame("should be the correct item " + viewHolder
4358115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                                    , viewHolder.mBoundItem,
43595ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    adapter.mItems.get(viewHolder.mPosition));
43605ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                            assertFalse("view should not be marked as removed",
43615ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
43625ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                                            .isItemRemoved());
43635ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                        }
43645ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                    }
43655ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                }
43665ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                detachAndScrapAttachedViews(recycler);
43675ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                layoutRange(recycler, state.getItemCount() - 1, -1);
43685ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                layoutLatch.countDown();
43695ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            }
43705ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
43715ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            @Override
43725ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
43735ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar                return supportPredictive;
43745ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            }
43755ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        };
43765ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        recyclerView.setLayoutManager(lm);
43775ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.expectLayouts(1);
43785ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        setRecyclerView(recyclerView);
43795ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.waitForLayout(2);
43805ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        getInstrumentation().waitForIdleSync();
43815ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
43825ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        assertPositions.set(true);
43835ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.expectLayouts(supportPredictive ? 2 : 1);
43845ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        adapter.deleteAndNotify(new int[]{deleteStart, deleteCount - 1}, new int[]{deleteStart, 1});
43855ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar        lm.waitForLayout(2);
43865ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar    }
43875ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar
4388be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
43890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void typeForCache() throws Throwable {
439022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final AtomicInteger viewType = new AtomicInteger(1);
439122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final TestAdapter adapter = new TestAdapter(100) {
439222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
439322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public int getItemViewType(int position) {
439422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                return viewType.get();
439522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
439622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
439722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
439822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public long getItemId(int position) {
439922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                return mItems.get(position).mId;
440022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
440122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        };
440222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        adapter.setHasStableIds(true);
440322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final AtomicInteger layoutStart = new AtomicInteger(2);
440422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final int childCount = 10;
440522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
440622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
440722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
440822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                super.onLayoutChildren(recycler, state);
440922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                detachAndScrapAttachedViews(recycler);
441022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                layoutRange(recycler, layoutStart.get(), layoutStart.get() + childCount);
441122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                layoutLatch.countDown();
441222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
441322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        };
441422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
4415668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        recyclerView.setItemAnimator(null);
441622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        recyclerView.setAdapter(adapter);
441722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        recyclerView.setLayoutManager(lm);
441822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        recyclerView.setItemViewCacheSize(10);
441922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.expectLayouts(1);
442022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        setRecyclerView(recyclerView);
442122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.waitForLayout(2);
442222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        getInstrumentation().waitForIdleSync();
442322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        layoutStart.set(4); // trigger a cache for 3,4
442422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.expectLayouts(1);
442522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        requestLayoutOnUIThread(recyclerView);
442622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.waitForLayout(2);
442722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        //
442822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        viewType.incrementAndGet();
442922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        layoutStart.set(2); // go back to bring views from cache
443022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.expectLayouts(1);
443122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        adapter.mItems.remove(1);
4432cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.dispatchDataSetChanged();
443322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        lm.waitForLayout(2);
443442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
443522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            @Override
443622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            public void run() {
443722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                for (int i = 2; i < 4; i++) {
4438115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    RecyclerView.ViewHolder vh = recyclerView.findViewHolderForLayoutPosition(i);
443922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                    assertEquals("View holder's type should match latest type", viewType.get(),
444022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                            vh.getItemViewType());
444122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                }
444222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            }
444322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        });
444422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar    }
444522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
4446be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
44470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void typeForExistingViews() throws Throwable {
44487e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final AtomicInteger viewType = new AtomicInteger(1);
44497e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final int invalidatedCount = 2;
44507e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final int layoutStart = 2;
44517e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final TestAdapter adapter = new TestAdapter(100) {
44527e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
44537e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            public int getItemViewType(int position) {
44547e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                return viewType.get();
44557e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
44567e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
44577e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
44588a11e6829c522aa1efcc903afa4c01d337082eabChris Craik            public void onBindViewHolder(@NonNull TestViewHolder holder,
44597e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    int position) {
44607e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                super.onBindViewHolder(holder, position);
44617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                if (position >= layoutStart && position < invalidatedCount + layoutStart) {
44627e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    try {
44637e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                        assertEquals("holder type should match current view type at position " +
44647e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                                position, viewType.get(), holder.getItemViewType());
44657e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    } catch (Throwable t) {
44667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                        postExceptionToInstrumentation(t);
44677e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    }
44687e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                }
44697e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
44707e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
44717e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
44727e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            public long getItemId(int position) {
44737e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                return mItems.get(position).mId;
44747e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
44757e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        };
44767e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        adapter.setHasStableIds(true);
44777e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
44787e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final int childCount = 10;
44797e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final TestLayoutManager lm = new TestLayoutManager() {
44807e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            @Override
44817e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
44827e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                super.onLayoutChildren(recycler, state);
44837e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                detachAndScrapAttachedViews(recycler);
44847e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                layoutRange(recycler, layoutStart, layoutStart + childCount);
44857e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                layoutLatch.countDown();
44867e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
44877e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        };
44887e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
44897e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        recyclerView.setAdapter(adapter);
44907e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        recyclerView.setLayoutManager(lm);
44917e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.expectLayouts(1);
44927e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        setRecyclerView(recyclerView);
44937e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.waitForLayout(2);
44947e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        getInstrumentation().waitForIdleSync();
44957e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        viewType.incrementAndGet();
44967e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.expectLayouts(1);
4497cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        adapter.changeAndNotify(layoutStart, invalidatedCount);
44987e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        lm.waitForLayout(2);
44997e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        checkForMainThreadException();
45007e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar    }
45017e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
45027e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
4503be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
45040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void state() throws Throwable {
4505d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final TestAdapter adapter = new TestAdapter(10);
4506d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final RecyclerView recyclerView = new RecyclerView(getActivity());
4507d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        recyclerView.setAdapter(adapter);
4508d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        recyclerView.setItemAnimator(null);
4509d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final AtomicInteger itemCount = new AtomicInteger();
4510d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final AtomicBoolean structureChanged = new AtomicBoolean();
4511d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        TestLayoutManager testLayoutManager = new TestLayoutManager() {
4512d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
4513d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
45148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                detachAndScrapAttachedViews(recycler);
4515d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                layoutRange(recycler, 0, state.getItemCount());
4516d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                itemCount.set(state.getItemCount());
4517d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.set(state.didStructureChange());
4518d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                layoutLatch.countDown();
4519d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
4520d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        };
4521d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        recyclerView.setLayoutManager(testLayoutManager);
4522d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        testLayoutManager.expectLayouts(1);
452342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
4524d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
4525d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            public void run() {
4526a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar                getActivity().getContainer().addView(recyclerView);
4527d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
4528d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        });
452966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        testLayoutManager.waitForLayout(2);
4530d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
4531d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("item count in state should be correct", adapter.getItemCount()
4532d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                , itemCount.get());
4533d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("structure changed should be true for first layout", true,
4534d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
4535d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        Thread.sleep(1000); //wait for other layouts.
4536d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        testLayoutManager.expectLayouts(1);
453742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
4538d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
4539d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            public void run() {
4540d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                recyclerView.requestLayout();
4541d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
4542d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        });
45438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.waitForLayout(2);
4544d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("in second layout,structure changed should be false", false,
4545d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
45468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.expectLayouts(1); //
4547b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        adapter.deleteAndNotify(3, 2);
45488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.waitForLayout(2);
4549d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("when items are removed, item count in state should be updated",
4550d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                adapter.getItemCount(),
4551d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                itemCount.get());
4552d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("structure changed should be true when items are removed", true,
4553d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
45548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.expectLayouts(1);
4555b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        adapter.addAndNotify(2, 5);
45568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        testLayoutManager.waitForLayout(2);
4557d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
4558d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("when items are added, item count in state should be updated",
4559d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                adapter.getItemCount(),
4560d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                itemCount.get());
4561d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        assertEquals("structure changed should be true when items are removed", true,
4562d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                structureChanged.get());
45630b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
45640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
4565be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
45660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void detachWithoutLayoutManager() throws Throwable {
4567abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
456842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
4569abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar            @Override
4570abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar            public void run() {
4571abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                try {
4572abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    setRecyclerView(recyclerView);
4573abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    removeRecyclerView();
4574abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                } catch (Throwable t) {
4575abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    postExceptionToInstrumentation(t);
4576abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                }
4577abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar            }
4578abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        });
4579abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar        checkForMainThreadException();
4580abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar    }
4581abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar
4582be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
45830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void updateHiddenView() throws Throwable {
45841a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
45851a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final int[] preLayoutRange = new int[]{0, 10};
45861a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final int[] postLayoutRange = new int[]{0, 10};
45871a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final AtomicBoolean enableGetViewTest = new AtomicBoolean(false);
4588959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        final List<Integer> disappearingPositions = new ArrayList<>();
45891a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final TestLayoutManager tlm = new TestLayoutManager() {
45901a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            @Override
45911a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            public boolean supportsPredictiveItemAnimations() {
45921a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                return true;
45931a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            }
45941a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
45951a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            @Override
45961a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
45971a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                try {
45981a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    final int[] layoutRange = state.isPreLayout() ? preLayoutRange
45991a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            : postLayoutRange;
46001a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    detachAndScrapAttachedViews(recycler);
46011a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    layoutRange(recycler, layoutRange[0], layoutRange[1]);
46021a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    if (!state.isPreLayout()) {
46031a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                        for (Integer position : disappearingPositions) {
46041a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            // test sanity.
46051a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            assertNull(findViewByPosition(position));
46061a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            final View view = recycler.getViewForPosition(position);
4607959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                            assertNotNull(view);
46081a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            addDisappearingView(view);
46091a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            measureChildWithMargins(view, 0, 0);
46101a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            // position item out of bounds.
46111a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                            view.layout(0, -500, view.getMeasuredWidth(),
46121a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                                    -500 + view.getMeasuredHeight());
46131a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                        }
46141a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    }
46151a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                } catch (Throwable t) {
46161a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    postExceptionToInstrumentation(t);
46171a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                }
46181a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                layoutLatch.countDown();
46191a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            }
46201a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        };
4621959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        recyclerView.getItemAnimator().setMoveDuration(4000);
4622959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        recyclerView.getItemAnimator().setRemoveDuration(4000);
46231a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        final TestAdapter adapter = new TestAdapter(100);
46241a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        recyclerView.setAdapter(adapter);
46251a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        recyclerView.setLayoutManager(tlm);
46261a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.expectLayouts(1);
46271a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        setRecyclerView(recyclerView);
46281a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.waitForLayout(1);
46291a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        checkForMainThreadException();
46301a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        // now, a child disappears
46311a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        disappearingPositions.add(0);
46321a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        // layout one shifted
46331a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        postLayoutRange[0] = 1;
46341a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        postLayoutRange[1] = 11;
46351a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.expectLayouts(2);
46361a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        adapter.addAndNotify(8, 1);
46371a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.waitForLayout(2);
46381a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        checkForMainThreadException();
46391a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
46401a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.expectLayouts(2);
46411a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        disappearingPositions.clear();
46421a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        // now that item should be moving, invalidate it and delete it.
46431a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        enableGetViewTest.set(true);
464442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
46451a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            @Override
46461a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            public void run() {
46471a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                try {
4648959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    assertThat("test sanity, should still be animating",
4649959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                            mRecyclerView.isAnimating(), CoreMatchers.is(true));
46501a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    adapter.changeAndNotify(0, 1);
46511a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                    adapter.deleteAndNotify(0, 1);
46521a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                } catch (Throwable throwable) {
4653959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar                    fail(throwable.getMessage());
46541a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar                }
46551a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar            }
46561a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        });
46571a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        tlm.waitForLayout(2);
46581a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar        checkForMainThreadException();
46591a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar    }
46601a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
4661be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
46620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnTop() throws Throwable {
46636d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.TOP);
46646d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
46656d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
4666be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
46670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnLeft() throws Throwable {
46686d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.LEFT);
46696d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
46706d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
4671be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
46720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnRight() throws Throwable {
46736d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.RIGHT);
46746d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
46756d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
4676be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
46770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnBottom() throws Throwable {
46786d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.BOTTOM);
46796d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
46806d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
4681be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
46820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnLeftRTL() throws Throwable {
46836d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.LEFT, true);
46846d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals("test sanity", ViewCompat.LAYOUT_DIRECTION_RTL,
46856d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                mRecyclerView.getLayoutManager().getLayoutDirection());
46866d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
46876d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
4688be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
46890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusBigViewOnRightRTL() throws Throwable {
46906d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(Gravity.RIGHT, true);
46916d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals("test sanity", ViewCompat.LAYOUT_DIRECTION_RTL,
46926d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                mRecyclerView.getLayoutManager().getLayoutDirection());
46936d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
46946d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
46956d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    public void focusTooBigViewTest(final int gravity) throws Throwable {
46966d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        focusTooBigViewTest(gravity, false);
46976d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
4698be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar
46996d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    public void focusTooBigViewTest(final int gravity, final boolean rtl) throws Throwable {
47006d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
47016d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        if (rtl) {
47026d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            ViewCompat.setLayoutDirection(rv, ViewCompat.LAYOUT_DIRECTION_RTL);
47036d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        }
47046d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger vScrollDist = new AtomicInteger(0);
47056d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger hScrollDist = new AtomicInteger(0);
47066d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger vDesiredDist = new AtomicInteger(0);
47076d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        final AtomicInteger hDesiredDist = new AtomicInteger(0);
47086d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
47096d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
47106d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
47116d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public int getLayoutDirection() {
47126d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return rtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR;
47136d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
47146d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
47156d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
47166d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
47176d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                detachAndScrapAttachedViews(recycler);
47186d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                final View view = recycler.getViewForPosition(0);
47196d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                addView(view);
47206d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                int left = 0, top = 0;
47216d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                view.setBackgroundColor(Color.rgb(0, 0, 255));
47226d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                switch (gravity) {
47236d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.LEFT:
47246d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.RIGHT:
47256d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        view.measure(
47266d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getWidth() * 1.5),
47276d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.EXACTLY),
47286d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getHeight() * .9),
47296d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.AT_MOST));
47306d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        left = gravity == Gravity.LEFT ? getWidth() - view.getMeasuredWidth() - 80
47316d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                : 90;
47326d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        top = 0;
47336d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        if (ViewCompat.LAYOUT_DIRECTION_RTL == getLayoutDirection()) {
47346d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                            hDesiredDist.set((left + view.getMeasuredWidth()) - getWidth());
47356d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        } else {
47366d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                            hDesiredDist.set(left);
47376d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        }
47386d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        break;
47396d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.TOP:
47406d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    case Gravity.BOTTOM:
47416d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        view.measure(
47426d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getWidth() * .9),
47436d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.AT_MOST),
47446d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                View.MeasureSpec.makeMeasureSpec((int) (getHeight() * 1.5),
47456d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                        View.MeasureSpec.EXACTLY));
47466d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        top = gravity == Gravity.TOP ? getHeight() - view.getMeasuredHeight() -
47476d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                                80 : 90;
47486d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        left = 0;
47496d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        vDesiredDist.set(top);
47506d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        break;
47516d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                }
47526d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
47536d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                view.layout(left, top, left + view.getMeasuredWidth(),
47546d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                        top + view.getMeasuredHeight());
47556d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                layoutLatch.countDown();
47566d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
47576d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
47586d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
47596d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public boolean canScrollVertically() {
47606d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return true;
47616d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
47626d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
47636d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
47646d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public boolean canScrollHorizontally() {
47656d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return super.canScrollHorizontally();
47666d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
47676d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
47686d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
47696d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
47706d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    RecyclerView.State state) {
47716d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                vScrollDist.addAndGet(dy);
47726d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                getChildAt(0).offsetTopAndBottom(-dy);
47736d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return dy;
47746d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
47756d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
47766d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            @Override
47776d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
47786d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                    RecyclerView.State state) {
47796d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                hScrollDist.addAndGet(dx);
47806d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                getChildAt(0).offsetLeftAndRight(-dx);
47816d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar                return dx;
47826d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            }
47836d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        };
47846d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
47856d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        rv.setAdapter(adapter);
47866d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        rv.setLayoutManager(tlm);
47876d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        tlm.expectLayouts(1);
47886d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        setRecyclerView(rv);
47896d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        tlm.waitForLayout(2);
47906d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        View view = rv.getChildAt(0);
4791959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", requestFocus(view, true));
4792959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("test sanity", view.hasFocus());
47936d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals(vDesiredDist.get(), vScrollDist.get());
47946d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals(hDesiredDist.get(), hScrollDist.get());
47956d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        assertEquals(mRecyclerView.getPaddingTop(), view.getTop());
47966d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        if (rtl) {
4797be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar            assertEquals(mRecyclerView.getWidth() - mRecyclerView.getPaddingRight(),
4798be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar                    view.getRight());
47996d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        } else {
48006d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar            assertEquals(mRecyclerView.getPaddingLeft(), view.getLeft());
48016d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar        }
48026d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar    }
48036d109dd6676f6bca949643d2d04365494eaeb867Yigit Boyar
4804be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
48050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void firstLayoutWithAdapterChanges() throws Throwable {
48064bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        final TestAdapter adapter = new TestAdapter(0);
48074bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
48084bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        setVisibility(rv, View.GONE);
48094bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
48104bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            @Override
48114bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
48124bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                try {
48134bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    super.onLayoutChildren(recycler, state);
48144bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    layoutRange(recycler, 0, state.getItemCount());
48154bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                } catch (Throwable t) {
48164bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    postExceptionToInstrumentation(t);
48174bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                } finally {
48184bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    layoutLatch.countDown();
48194bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                }
48204bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            }
48214bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar
48224bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            @Override
48234bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            public boolean supportsPredictiveItemAnimations() {
48244bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                return true;
48254bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            }
48264bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        };
48274bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        rv.setLayoutManager(tlm);
48284bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        rv.setAdapter(adapter);
48294bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        rv.setHasFixedSize(true);
48304bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        setRecyclerView(rv);
48314bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        tlm.expectLayouts(1);
48324bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        tlm.assertNoLayout("test sanity, layout should not run", 1);
48334bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        getInstrumentation().waitForIdleSync();
483442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
48354bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            @Override
48364bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            public void run() {
48374bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                try {
48384bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    adapter.addAndNotify(2);
48394bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                } catch (Throwable throwable) {
48404bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                    throwable.printStackTrace();
48414bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                }
48424bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar                rv.setVisibility(View.VISIBLE);
48434bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar            }
48444bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        });
48454bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        checkForMainThreadException();
48464bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        tlm.waitForLayout(2);
48474bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        assertEquals(2, rv.getChildCount());
48484bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar        checkForMainThreadException();
48494bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar    }
48504bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar
48514bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar    @Test
485232b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar    public void computeScrollOffsetWithoutLayoutManager() throws Throwable {
485314d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
485414d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        rv.setAdapter(new TestAdapter(10));
485514d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        setRecyclerView(rv);
485614d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollExtent());
485714d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollOffset());
485814d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollRange());
485914d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
486014d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollExtent());
486114d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollOffset());
486214d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollRange());
486314d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    }
486414d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
486514d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    @Test
486632b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar    public void computeScrollOffsetWithoutAdapter() throws Throwable {
486714d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
486814d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        rv.setLayoutManager(new TestLayoutManager());
486914d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        setRecyclerView(rv);
487014d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollExtent());
487114d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollOffset());
487214d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeHorizontalScrollRange());
487314d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
487414d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollExtent());
487514d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollOffset());
487614d75838c4578e079d75ea149e9833250c5d741bYigit Boyar        assertEquals(0, rv.computeVerticalScrollRange());
487714d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    }
487814d75838c4578e079d75ea149e9833250c5d741bYigit Boyar
487914d75838c4578e079d75ea149e9833250c5d741bYigit Boyar    @Test
48800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusRectOnScreenWithDecorOffsets() throws Throwable {
4881f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        focusRectOnScreenTest(true);
4882f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    }
4883f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
4884be7a54a6e02c9230a08e63f1c964907d129b6a10Yigit Boyar    @Test
48850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void focusRectOnScreenWithout() throws Throwable {
4886f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        focusRectOnScreenTest(false);
4887f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    }
4888f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
4889f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    public void focusRectOnScreenTest(boolean addItemDecors) throws Throwable {
4890f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
4891f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        final AtomicInteger scrollDist = new AtomicInteger(0);
4892f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
4893f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            @Override
4894f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
4895f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                detachAndScrapAttachedViews(recycler);
4896f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                final View view = recycler.getViewForPosition(0);
4897f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                addView(view);
4898f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                measureChildWithMargins(view, 0, 0);
4899c0629f332edcfead292c08612cde8ba234c4e77cshepshapard                view.layout(0, -20, view.getMeasuredWidth(),
4900c0629f332edcfead292c08612cde8ba234c4e77cshepshapard                        -20 + view.getMeasuredHeight()); // ignore decors on purpose
4901f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                layoutLatch.countDown();
4902f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            }
4903f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
4904f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            @Override
4905f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            public boolean canScrollVertically() {
4906f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                return true;
4907f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            }
4908f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
4909f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            @Override
4910f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
4911f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                    RecyclerView.State state) {
4912f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                scrollDist.addAndGet(dy);
4913f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                return dy;
4914f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            }
4915f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        };
4916f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
4917f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        if (addItemDecors) {
4918f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            rv.addItemDecoration(new RecyclerView.ItemDecoration() {
4919f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                @Override
4920f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
4921f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                        RecyclerView.State state) {
4922f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                    outRect.set(0, 10, 0, 10);
4923f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar                }
4924f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar            });
4925f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        }
4926f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        rv.setAdapter(adapter);
4927f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        rv.setLayoutManager(tlm);
4928f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        tlm.expectLayouts(1);
4929f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        setRecyclerView(rv);
4930f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        tlm.waitForLayout(2);
4931f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
4932f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        View view = rv.getChildAt(0);
4933959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        requestFocus(view, true);
4934f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar        assertEquals(addItemDecors ? -30 : -20, scrollDist.get());
4935f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar    }
4936f225a3b785fe24c31aa5f75501754b974e106859Yigit Boyar
4937e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    @Test
49380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void unimplementedSmoothScroll() throws Throwable {
4939e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final AtomicInteger receivedScrollToPosition = new AtomicInteger(-1);
4940e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final AtomicInteger receivedSmoothScrollToPosition = new AtomicInteger(-1);
4941e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final CountDownLatch cbLatch = new CountDownLatch(2);
4942e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
4943e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
4944e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
4945e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                detachAndScrapAttachedViews(recycler);
4946e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                layoutRange(recycler, 0, 10);
4947e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                layoutLatch.countDown();
4948e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
4949e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
4950e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
4951e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
4952e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                    int position) {
4953e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                assertEquals(-1, receivedSmoothScrollToPosition.get());
4954e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                receivedSmoothScrollToPosition.set(position);
4955e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                RecyclerView.SmoothScroller ss =
4956e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        new LinearSmoothScroller(recyclerView.getContext()) {
4957e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
4958678ce8206279d0ea2e6425fbb02ec9183d93d8edAurimas Liutikas                            public PointF computeScrollVectorForPosition(int targetPosition) {
4959678ce8206279d0ea2e6425fbb02ec9183d93d8edAurimas Liutikas                                return null;
4960e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
4961e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        };
4962e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                ss.setTargetPosition(position);
4963e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                startSmoothScroll(ss);
4964e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                cbLatch.countDown();
4965e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
4966e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
4967e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
4968e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void scrollToPosition(int position) {
4969e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                assertEquals(-1, receivedScrollToPosition.get());
4970e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                receivedScrollToPosition.set(position);
4971e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                cbLatch.countDown();
4972e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
4973e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        };
4974e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        RecyclerView rv = new RecyclerView(getActivity());
4975e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setAdapter(new TestAdapter(100));
4976e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setLayoutManager(tlm);
4977e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.expectLayouts(1);
4978e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        setRecyclerView(rv);
4979e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.waitForLayout(2);
4980ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
4981959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        smoothScrollToPosition(35, false);
4982ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("smoothScrollToPosition should be ignored when frozen",
4983ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                -1, receivedSmoothScrollToPosition.get());
4984ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
4985959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        smoothScrollToPosition(35, false);
4986e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertTrue("both scrolls should be called", cbLatch.await(3, TimeUnit.SECONDS));
4987e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        checkForMainThreadException();
4988e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertEquals(35, receivedSmoothScrollToPosition.get());
4989e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertEquals(35, receivedScrollToPosition.get());
4990e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
4991e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
4992e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    @Test
49930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void jumpingJackSmoothScroller() throws Throwable {
4994e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        jumpingJackSmoothScrollerTest(true);
4995e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
4996e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
4997e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    @Test
49980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void jumpingJackSmoothScrollerGoesIdle() throws Throwable {
4999e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        jumpingJackSmoothScrollerTest(false);
5000e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
5001e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
50027f5e94985f795f81884334ca2752a0469a998762Yigit Boyar    @Test
50037f5e94985f795f81884334ca2752a0469a998762Yigit Boyar    public void testScrollByBeforeFirstLayout() throws Throwable {
50047f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        final RecyclerView recyclerView = new RecyclerView(getActivity());
50057f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        TestAdapter adapter = new TestAdapter(10);
50067f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        recyclerView.setLayoutManager(new TestLayoutManager() {
50077f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            AtomicBoolean didLayout = new AtomicBoolean(false);
50087f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
50097f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
50107f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                super.onLayoutChildren(recycler, state);
50117f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                didLayout.set(true);
50127f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
50137f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
50147f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
50157f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
50167f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    RecyclerView.State state) {
50177f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                assertThat("should run layout before scroll",
50187f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                        didLayout.get(), CoreMatchers.is(true));
50197f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                return super.scrollVerticallyBy(dy, recycler, state);
50207f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
50217f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
50227f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
50237f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public boolean canScrollVertically() {
50247f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                return true;
50257f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
50267f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        });
50277f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        recyclerView.setAdapter(adapter);
50287f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
502942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
50307f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            @Override
50317f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            public void run() {
50327f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                try {
50337f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    setRecyclerView(recyclerView);
50347f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    recyclerView.scrollBy(10, 19);
50357f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                } catch (Throwable throwable) {
50367f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                    postExceptionToInstrumentation(throwable);
50377f5e94985f795f81884334ca2752a0469a998762Yigit Boyar                }
50387f5e94985f795f81884334ca2752a0469a998762Yigit Boyar            }
50397f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        });
50407f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
50417f5e94985f795f81884334ca2752a0469a998762Yigit Boyar        checkForMainThreadException();
50427f5e94985f795f81884334ca2752a0469a998762Yigit Boyar    }
50437f5e94985f795f81884334ca2752a0469a998762Yigit Boyar
5044e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    private void jumpingJackSmoothScrollerTest(final boolean succeed) throws Throwable {
5045e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final List<Integer> receivedScrollToPositions = new ArrayList<>();
5046e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final TestAdapter testAdapter = new TestAdapter(200);
5047e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final AtomicBoolean mTargetFound = new AtomicBoolean(false);
5048e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        TestLayoutManager tlm = new TestLayoutManager() {
5049e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            int pendingScrollPosition = -1;
5050e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
5051e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
5052e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                detachAndScrapAttachedViews(recycler);
5053e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                final int pos = pendingScrollPosition < 0 ? 0: pendingScrollPosition;
5054e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                layoutRange(recycler, pos, pos + 10);
5055e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                if (layoutLatch != null) {
5056e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                    layoutLatch.countDown();
5057e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                }
5058e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
5059e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
5060e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
5061e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
5062e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                    final int position) {
5063e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                RecyclerView.SmoothScroller ss =
5064e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        new LinearSmoothScroller(recyclerView.getContext()) {
5065e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
5066678ce8206279d0ea2e6425fbb02ec9183d93d8edAurimas Liutikas                            public PointF computeScrollVectorForPosition(int targetPosition) {
5067678ce8206279d0ea2e6425fbb02ec9183d93d8edAurimas Liutikas                                return new PointF(0, 1);
5068e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
5069e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
5070e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
5071e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            protected void onTargetFound(View targetView, RecyclerView.State state,
50729c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri                                    Action action) {
5073e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                super.onTargetFound(targetView, state, action);
5074e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                mTargetFound.set(true);
5075e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
5076e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
5077e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            @Override
5078e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            protected void updateActionForInterimTarget(Action action) {
5079e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                int limit = succeed ? getTargetPosition() : 100;
5080e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                if (pendingScrollPosition + 2 < limit) {
5081e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                    if (pendingScrollPosition != NO_POSITION) {
5082e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                        assertEquals(pendingScrollPosition,
5083e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                                getChildViewHolderInt(getChildAt(0))
5084e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                                        .getAdapterPosition());
5085e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                    }
5086e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                    action.jumpTo(pendingScrollPosition + 2);
5087e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                                }
5088e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                            }
5089e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                        };
5090e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                ss.setTargetPosition(position);
5091e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                startSmoothScroll(ss);
5092e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
5093e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
5094e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
5095e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void scrollToPosition(int position) {
5096e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                receivedScrollToPositions.add(position);
5097e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                pendingScrollPosition = position;
5098e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                requestLayout();
5099e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
5100e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        };
5101e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        final RecyclerView rv = new RecyclerView(getActivity());
5102e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setAdapter(testAdapter);
5103e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        rv.setLayoutManager(tlm);
5104e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
5105e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.expectLayouts(1);
5106e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        setRecyclerView(rv);
5107e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        tlm.waitForLayout(2);
5108e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
510942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
5110e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            @Override
5111e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            public void run() {
5112e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar                rv.smoothScrollToPosition(150);
5113e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            }
5114e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        });
5115e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        int limit = 100;
5116e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        while (rv.getLayoutManager().isSmoothScrolling() && --limit > 0) {
5117e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            Thread.sleep(200);
5118e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            checkForMainThreadException();
5119e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        }
5120e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        checkForMainThreadException();
5121e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertTrue(limit > 0);
5122e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        for (int i = 1; i < 100; i+=2) {
5123e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar            assertTrue("scroll positions must include " + i, receivedScrollToPositions.contains(i));
5124e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        }
5125e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
5126e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar        assertEquals(succeed, mTargetFound.get());
5127e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
5128e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar    }
5129e1cd8d86d84646768867eeaeb038e1c135ca3e4cYigit Boyar
51300b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    private static class TestViewHolder2 extends RecyclerView.ViewHolder {
5131824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
5132824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        Object mData;
5133824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
51340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        public TestViewHolder2(View itemView) {
51350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            super(itemView);
51360b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
51370b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
5138d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
51390b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    private static class TestAdapter2 extends RecyclerView.Adapter<TestViewHolder2> {
5140824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar
51410b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        List<Item> mItems;
51420b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
51430b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        private TestAdapter2(int count) {
514432b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar            mItems = new ArrayList<>(count);
51450b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            for (int i = 0; i < count; i++) {
51460b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                mItems.add(new Item(i, "Item " + i));
51470b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
51480b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
51490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
51500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        @Override
51518a11e6829c522aa1efcc903afa4c01d337082eabChris Craik        public TestViewHolder2 onCreateViewHolder(@NonNull ViewGroup parent,
51520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                int viewType) {
51530b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            return new TestViewHolder2(new TextView(parent.getContext()));
51540b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
51550b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
51560b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        @Override
51578a11e6829c522aa1efcc903afa4c01d337082eabChris Craik        public void onBindViewHolder(@NonNull TestViewHolder2 holder, int position) {
51580b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            final Item item = mItems.get(position);
51590b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")");
51600b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
51610b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
51620b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        @Override
51630b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        public int getItemCount() {
51640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            return mItems.size();
51650b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
5166d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
5167d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
5168e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar    public interface AdapterRunnable {
51691a2a95faea3d32075b8506f13f6d6f8292db51b6Yigit Boyar
51703d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        void run(TestAdapter adapter) throws Throwable;
5171115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar    }
5172115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
51733d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    public class LayoutAllLayoutManager extends TestLayoutManager {
517434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        private final boolean mAllowNullLayoutLatch;
5175c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        public int onItemsChangedCallCount = 0;
5176c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        public int onAdapterChagnedCallCount = 0;
517734a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
517834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        public LayoutAllLayoutManager() {
517934a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            // by default, we don't allow unexpected layouts.
518034a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            this(false);
518134a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        }
518234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        public LayoutAllLayoutManager(boolean allowNullLayoutLatch) {
518334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            mAllowNullLayoutLatch = allowNullLayoutLatch;
518434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar        }
518534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
5186c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        @Override
5187c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        public void onItemsChanged(RecyclerView recyclerView) {
5188c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard            super.onItemsChanged(recyclerView);
5189c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard            onItemsChangedCallCount++;
5190c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        }
5191c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard
5192c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        @Override
5193c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        public void onAdapterChanged(RecyclerView.Adapter oldAdapter,
5194c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard                RecyclerView.Adapter newAdapter) {
5195c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard            super.onAdapterChanged(oldAdapter, newAdapter);
5196c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard            onAdapterChagnedCallCount++;
5197c6257630a384dd28b8670011806d8f4f2fcd5840shepshapard        }
519834a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar
51993d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        @Override
52003d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
52013e61562902afe237012a2d01ac0468d6b70268f4Yigit Boyar            detachAndScrapAttachedViews(recycler);
52023d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar            layoutRange(recycler, 0, state.getItemCount());
520334a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            if (!mAllowNullLayoutLatch || layoutLatch != null) {
520434a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                layoutLatch.countDown();
520534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar            }
52063d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar        }
52073d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar    }
5208bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
5209bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    /**
5210bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar     * Proxy class to make protected methods public
5211bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar     */
5212bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    public static class TestRecyclerView extends RecyclerView {
5213bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
5214bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public TestRecyclerView(Context context) {
5215bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super(context);
5216bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
5217bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
5218bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public TestRecyclerView(Context context, @Nullable AttributeSet attrs) {
5219bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super(context, attrs);
5220bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
5221bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
5222bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public TestRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
5223bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super(context, attrs, defStyle);
5224bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
5225bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
5226bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        @Override
5227bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public void detachViewFromParent(int index) {
5228bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super.detachViewFromParent(index);
5229bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
5230bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar
5231bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        @Override
5232bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        public void attachViewToParent(View child, int index, ViewGroup.LayoutParams params) {
5233bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar            super.attachViewToParent(child, index, params);
5234bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar        }
5235bd1424dffe99916b724feef9fec6d40639c6b779Yigit Boyar    }
52364510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
523732b80d09635250fca4ffa34d55a3a68d5dca9cb5Yigit Boyar    private interface ViewRunnable {
52384510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        void run(View view) throws RuntimeException;
52394510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
524076daed103193a1756535d1f59b165e98e1d17445Chris Banes
524176daed103193a1756535d1f59b165e98e1d17445Chris Banes    public static class FullyConsumingNestedScroller extends NestedScrollingParent2Adapter {
524276daed103193a1756535d1f59b165e98e1d17445Chris Banes        @Override
524376daed103193a1756535d1f59b165e98e1d17445Chris Banes        public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
524476daed103193a1756535d1f59b165e98e1d17445Chris Banes                @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) {
524576daed103193a1756535d1f59b165e98e1d17445Chris Banes            // Always start regardless of type
524676daed103193a1756535d1f59b165e98e1d17445Chris Banes            return true;
524776daed103193a1756535d1f59b165e98e1d17445Chris Banes        }
524876daed103193a1756535d1f59b165e98e1d17445Chris Banes
524976daed103193a1756535d1f59b165e98e1d17445Chris Banes        @Override
525076daed103193a1756535d1f59b165e98e1d17445Chris Banes        public void onNestedPreScroll(@NonNull View target, int dx, int dy,
5251046707494799307838f1dad4a69b8526d7980b27Jake Wharton                @NonNull int[] consumed, @ViewCompat.NestedScrollType int type) {
525276daed103193a1756535d1f59b165e98e1d17445Chris Banes            // Consume everything!
525376daed103193a1756535d1f59b165e98e1d17445Chris Banes            consumed[0] = dx;
525476daed103193a1756535d1f59b165e98e1d17445Chris Banes            consumed[1] = dy;
525576daed103193a1756535d1f59b165e98e1d17445Chris Banes        }
525676daed103193a1756535d1f59b165e98e1d17445Chris Banes
525776daed103193a1756535d1f59b165e98e1d17445Chris Banes        @Override
525876daed103193a1756535d1f59b165e98e1d17445Chris Banes        public int getNestedScrollAxes() {
525976daed103193a1756535d1f59b165e98e1d17445Chris Banes            return ViewCompat.SCROLL_AXIS_VERTICAL | ViewCompat.SCROLL_AXIS_HORIZONTAL;
526076daed103193a1756535d1f59b165e98e1d17445Chris Banes        }
526176daed103193a1756535d1f59b165e98e1d17445Chris Banes
526276daed103193a1756535d1f59b165e98e1d17445Chris Banes        @Override
526376daed103193a1756535d1f59b165e98e1d17445Chris Banes        public void onStopNestedScroll(View target) {
526476daed103193a1756535d1f59b165e98e1d17445Chris Banes            super.onStopNestedScroll(target);
526576daed103193a1756535d1f59b165e98e1d17445Chris Banes        }
526676daed103193a1756535d1f59b165e98e1d17445Chris Banes
526776daed103193a1756535d1f59b165e98e1d17445Chris Banes        @Override
526876daed103193a1756535d1f59b165e98e1d17445Chris Banes        public void onStopNestedScroll(@NonNull View target,
526976daed103193a1756535d1f59b165e98e1d17445Chris Banes                @ViewCompat.NestedScrollType int type) {
527076daed103193a1756535d1f59b165e98e1d17445Chris Banes            super.onStopNestedScroll(target, type);
527176daed103193a1756535d1f59b165e98e1d17445Chris Banes        }
527276daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
527376daed103193a1756535d1f59b165e98e1d17445Chris Banes
52744c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu    @Test
52754c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu    public void testRemainingScrollInLayout() throws Throwable {
52764c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        final RecyclerView recyclerView = new RecyclerView(getActivity());
52774c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        final TestAdapter adapter = new TestAdapter(100);
52784c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu
52794c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        final CountDownLatch firstScrollDone = new CountDownLatch(1);
52804c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        final CountDownLatch scrollFinished = new CountDownLatch(1);
52814c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        final int[] totalScrollDistance = new int[] {0};
52824c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        recyclerView.setLayoutManager(new TestLayoutManager() {
52834c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            @Override
52844c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
52854c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                if (firstScrollDone.getCount() < 1 && scrollFinished.getCount() == 1) {
52864c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    try {
52874c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                        assertTrue("layout pass has remaining scroll",
52884c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                                state.getRemainingScrollVertical() != 0);
52894c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                        assertEquals("layout pass has remaining scroll",
52904c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                                1000 - totalScrollDistance[0], state.getRemainingScrollVertical());
52914c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    } catch (Throwable throwable) {
52924c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                        postExceptionToInstrumentation(throwable);
52934c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    }
52944c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                }
52954c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                super.onLayoutChildren(recycler, state);
52964c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            }
52974c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu
52984c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            @Override
52994c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
53004c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    RecyclerView.State state) {
53014c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                firstScrollDone.countDown();
53024c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                totalScrollDistance[0] += dy;
53034c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                if (state.getRemainingScrollVertical() == 0) {
53044c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    // the last scroll pass will have remaining 0
53054c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    scrollFinished.countDown();
53064c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                }
53074c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                return super.scrollVerticallyBy(dy, recycler, state);
53084c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            }
53094c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu
53104c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            @Override
53114c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            public boolean canScrollVertically() {
53124c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                return true;
53134c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            }
53144c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        });
53154c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        recyclerView.setAdapter(adapter);
53164c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        mActivityRule.runOnUiThread(new Runnable() {
53174c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            @Override
53184c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            public void run() {
53194c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                try {
53204c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    setRecyclerView(recyclerView);
53214c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    recyclerView.smoothScrollBy(0, 1000);
53224c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                } catch (Throwable throwable) {
53234c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    postExceptionToInstrumentation(throwable);
53244c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                }
53254c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            }
53264c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        });
53274c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu
53284c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        firstScrollDone.await(1, TimeUnit.SECONDS);
53294c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        mActivityRule.runOnUiThread(new Runnable() {
53304c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            @Override
53314c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            public void run() {
53324c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                try {
53334c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    recyclerView.requestLayout();
53344c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                } catch (Throwable throwable) {
53354c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                    postExceptionToInstrumentation(throwable);
53364c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu                }
53374c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu            }
53384c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        });
53394c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        waitForIdleScroll(recyclerView);
53404c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        assertTrue(scrollFinished.getCount() < 1);
53414c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu        assertEquals(totalScrollDistance[0], 1000);
53424c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu    }
53434c5e22d736ff19351f4318a85e94c6d94c5f15b6Dake Gu
53447a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar}
5345