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