BaseRecyclerViewInstrumentationTest.java revision da50fde358bd3ef2edb273b3bf0265cf5e84ec6b
1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/* 2d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Copyright (C) 2014 The Android Open Source Project 3d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 4d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Licensed under the Apache License, Version 2.0 (the "License"); 5d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * you may not use this file except in compliance with the License. 6d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * You may obtain a copy of the License at 7d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 8d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * http://www.apache.org/licenses/LICENSE-2.0 9d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * 10d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Unless required by applicable law or agreed to in writing, software 11d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * distributed under the License is distributed on an "AS IS" BASIS, 12d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * See the License for the specific language governing permissions and 14d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * limitations under the License. 15d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase */ 16d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 17d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepackage android.support.v7.widget; 18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 19c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport android.graphics.Rect; 208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport android.os.Looper; 21e2437126f0225135891544315ff72e54c7de0f59Yigit Boyarimport android.support.v4.view.ViewCompat; 22d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2; 23d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log; 24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View; 25d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup; 26310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView; 28d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 29d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList; 3090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.HashSet; 31d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List; 3290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.Set; 33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch; 34d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 3511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 36da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyarimport java.util.concurrent.locks.Lock; 37da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyarimport java.util.concurrent.locks.ReentrantLock; 38d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 39d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends 40d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ActivityInstrumentationTestCase2<TestActivity> { 41d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 42d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase private static final String TAG = "RecyclerViewTest"; 43d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 44b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private boolean mDebug; 45b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 46b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected RecyclerView mRecyclerView; 47d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected AdapterHelper mAdapterHelper; 498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 507e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Throwable mainThreadException; 517e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 52d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 54b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 55b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 56b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super("android.support.v7.recyclerview", TestActivity.class); 58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void checkForMainThreadException() throws Throwable { 627e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mainThreadException != null) { 637e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar throw mainThreadException; 647e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 657e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 67e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar void setHasTransientState(final View view, final boolean value) { 68e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 69e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 70e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 71e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 72e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar ViewCompat.setHasTransientState(view, value); 73e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 74e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 75e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 76e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 77e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 78e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 79e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar 800b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void setAdapter(final RecyclerView.Adapter adapter) throws Throwable { 810b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 820b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 830b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 840b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setAdapter(adapter); 850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 860b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 870b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 880b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 890b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void swapAdapter(final RecyclerView.Adapter adapter, 900b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar final boolean removeAndRecycleExistingViews) throws Throwable { 910b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 920b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 930b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 94824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar try { 95824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 96824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } catch (Throwable t) { 97824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar postExceptionToInstrumentation(t); 98824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } 990b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1000b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 101824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar checkForMainThreadException(); 1020b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1030b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 1047e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void postExceptionToInstrumentation(Throwable t) { 1056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mainThreadException != null) { 1066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar Log.e(TAG, "receiving another main thread exception. dropping.", t); 1076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 10890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Log.e(TAG, "captured exception on main thread", t); 1096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mainThreadException = t; 1106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1127e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mRecyclerView != null && mRecyclerView 1137e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar .getLayoutManager() instanceof TestLayoutManager) { 1147e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager(); 1157e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar // finish all layouts so that we get the correct exception 1167e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar while (lm.layoutLatch.getCount() > 0) { 1177e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar lm.layoutLatch.countDown(); 1187e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1197e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1207e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1217e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 1228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 1238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected void tearDown() throws Exception { 1248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 1258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 1268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 1278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 1288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 1298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 132ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar super.tearDown(); 133ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar 1346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 1356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 1366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Exception e) { 1376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throw e; 1386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 139ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar throw new Exception(throwable); 1406e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 143c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public Rect getDecoratedRecyclerViewBounds() { 144c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new Rect( 145c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft(), 146c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop(), 147c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft() + mRecyclerView.getWidth(), 148c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() 149c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 150c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 151c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 152b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 153d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mRecyclerView == null) { 154d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 155d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 15690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar if (!isMainThread()) { 15790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar getInstrumentation().waitForIdleSync(); 15890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 159b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 160b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 161b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 162abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar try { 163abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar final RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); 164abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar if (adapter instanceof AttachDetachCountingAdapter) { 165abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar ((AttachDetachCountingAdapter) adapter).getCounter() 166abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar .validateRemaining(mRecyclerView); 167abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } 168abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar getActivity().mContainer.removeAllViews(); 169abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } catch (Throwable t) { 170abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar postExceptionToInstrumentation(t); 17190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 172b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 173b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 17490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mRecyclerView = null; 175b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 176b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 177504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar void waitForAnimations(int seconds) throws InterruptedException { 178504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final CountDownLatch latch = new CountDownLatch(2); 179504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar boolean running = mRecyclerView.mItemAnimator 180504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 181504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 182504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void onAnimationsFinished() { 183504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.countDown(); 184504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 185504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 186504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (running) { 187504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.await(seconds, TimeUnit.SECONDS); 188504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 189504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 190504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 191542f1260934df280985294eaef1ec8469863281fYigit Boyar public boolean requestFocus(final View view) { 192542f1260934df280985294eaef1ec8469863281fYigit Boyar final boolean[] result = new boolean[1]; 193542f1260934df280985294eaef1ec8469863281fYigit Boyar getActivity().runOnUiThread(new Runnable() { 194542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 195542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 196542f1260934df280985294eaef1ec8469863281fYigit Boyar result[0] = view.requestFocus(); 197542f1260934df280985294eaef1ec8469863281fYigit Boyar } 198542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 199542f1260934df280985294eaef1ec8469863281fYigit Boyar return result[0]; 200542f1260934df280985294eaef1ec8469863281fYigit Boyar } 201542f1260934df280985294eaef1ec8469863281fYigit Boyar 202b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 2030b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar setRecyclerView(recyclerView, true); 2040b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2050b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool) 2060b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar throws Throwable { 207e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar setRecyclerView(recyclerView, true, true); 208e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 209e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool, 210e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar boolean addPositionCheckItemAnimator) 211e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar throws Throwable { 212b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 2130b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (assignDummyPool) { 2140b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 2150b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 2160b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 2170b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType); 2180b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (viewHolder == null) { 2190b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return null; 2200b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2210b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND); 2220b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPosition = 200; 2230b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mOldPosition = 300; 2240b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPreLayoutPosition = 500; 2250b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return viewHolder; 2268da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar } 2278da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar 2280b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 2290b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 230619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNull(scrap.mOwnerRecyclerView); 2310b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar super.putRecycledView(scrap); 2320b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2330b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }; 2340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 2350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 236e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (addPositionCheckItemAnimator) { 237e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 238e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar @Override 239e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 240e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.State state) { 241e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.ViewHolder vh = parent.getChildViewHolder(view); 242e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (!vh.isRemoved()) { 243e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar assertNotSame("If getItemOffsets is called, child should have a valid" 244619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar + " adapter position unless it is removed : " + vh, 245e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar vh.getAdapterPosition(), RecyclerView.NO_POSITION); 246e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 247e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 248e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar }); 249e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 2508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 251b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 252b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 253b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 254b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 255b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 256b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 257b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 258b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 259310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar protected FrameLayout getRecyclerViewContainer() { 260310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return getActivity().mContainer; 261310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 263e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void requestLayoutOnUIThread(final View view) { 264e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 265e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 266e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 267e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 268e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar view.requestLayout(); 269e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 270e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 271e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 272e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 273e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 274b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 275b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 276e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void scrollBy(final int dt) { 277e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 278e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 279e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 280e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 281e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 282e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar mRecyclerView.scrollBy(dt, 0); 283e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } else { 284e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar mRecyclerView.scrollBy(0, dt); 285e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 286b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 287e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 288e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 289e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 290e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 291e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 292d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 293d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 2948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 2958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 2968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 2988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 2998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 3018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 3048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 305619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 306619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 307619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 3088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 3098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 3118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 3128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 314de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar getInstrumentation().waitForIdleSync(); 315de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar Thread.sleep(200); //give scroller some time so start 3168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 3178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 3188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 3198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 3208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 3228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 323619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 324619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 325619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 326504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 329d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 330d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 331115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item mBoundItem; 332d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 333d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 334d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 335310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar itemView.setFocusable(true); 336d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 337504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 338504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 339504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public String toString() { 340115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar return super.toString() + " item:" + mBoundItem; 341504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 342d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 343da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar class DumbLayoutManager extends TestLayoutManager { 344da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar ReentrantLock mLayoutLock = new ReentrantLock(); 345da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void blockLayout() { 346da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.lock(); 347da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 348d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 349da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void unblockLayout() { 350da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.unlock(); 351da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 352da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar @Override 353da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 354da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.lock(); 355da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar detachAndScrapAttachedViews(recycler); 356da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutRange(recycler, 0, state.getItemCount()); 357da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar if (layoutLatch != null) { 358da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutLatch.countDown(); 359da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 360da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.unlock(); 361da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 362da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 363d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestLayoutManager extends RecyclerView.LayoutManager { 364d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 365d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase CountDownLatch layoutLatch; 366d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 367d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 368d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 369d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 370d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 371b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 372b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 373d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 374d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 375e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar getInstrumentation().waitForIdleSync(); 376d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 377d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 378b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 379b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 380b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 381b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 382b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 383b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 384b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 385b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 386b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 387b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 388b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 3898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS); 390b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 391b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 392d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 393d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 394d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 395d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 396d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 397d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 398d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 399d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 400d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 401d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 402d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 403d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 404115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item item = ((TestViewHolder) lp.mViewHolder).mBoundItem; 405b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 406d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 407d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 4098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 4108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 4118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 4128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 413d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 414d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 415d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 416b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 417b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 418b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 419b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 420f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar void layoutRange(RecyclerView.Recycler recycler, int start, int end) { 421fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar assertScrap(recycler); 422b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 423d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 424d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4255ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 426504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int top = 0; 4275ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 428b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 429d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 430d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 431d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 432b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 433b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 434c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar if (!mRecyclerView.mState.isPreLayout()) { 435c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view 436c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar .getLayoutParams(); 437c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar assertFalse("In post layout, getViewForPosition should never return a view " 438c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar + "that is removed", layoutParams != null 439c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar && layoutParams.isItemRemoved()); 440c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 441b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 4428da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar assertEquals("getViewForPosition should return correct position", 4438da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar i, getPosition(view)); 444c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar addView(view); 445b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 446d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 447504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view) 448504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar , top + getDecoratedMeasuredHeight(view)); 449504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar top += view.getMeasuredHeight(); 450d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 451d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 452fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar 453fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar private void assertScrap(RecyclerView.Recycler recycler) { 454e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (mRecyclerView.getAdapter() != null && 455e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar !mRecyclerView.getAdapter().hasStableIds()) { 456e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) { 457e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid()); 458e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 459fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 460fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 461bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 462bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 463bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollHorizontally() { 464bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 465bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 466bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 467bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 468bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollVertically() { 469bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 470bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 471bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 472bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 473bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 474bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 475bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dx; 476bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 477bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 478bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 479bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 480bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 481bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dy; 482bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 483d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 484d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 485d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 48611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 48711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 488d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 4892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAdapterIndex; 490d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 4912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final String mText; 492d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 4932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Item(int adapterIndex, String text) { 4942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAdapterIndex = adapterIndex; 4952d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mText = text; 496d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 4998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 5008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 5018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 5022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", originalIndex=" + mAdapterIndex + 5032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", text='" + mText + '\'' + 5048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 5058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 506d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 507d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 50890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar class TestAdapter extends RecyclerView.Adapter<TestViewHolder> 50990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar implements AttachDetachCountingAdapter { 510d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 51190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter mAttachmentCounter = new ViewAttachDetachCounter(); 512d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 513d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 514d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter(int count) { 515d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 516d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = 0; i < count; i++) { 517d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems.add(new Item(i, "Item " + i)); 518d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 519d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 520d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 521d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 52290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttachedToWindow(TestViewHolder holder) { 52390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewAttachedToWindow(holder); 52490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewAttached(holder); 52590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 52690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 52790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 52890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetachedFromWindow(TestViewHolder holder) { 52990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewDetachedFromWindow(holder); 53090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewDetached(holder); 53190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 53290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 53390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 53490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttachedToRecyclerView(RecyclerView recyclerView) { 53590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onAttachedToRecyclerView(recyclerView); 53690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onAttached(recyclerView); 53790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 53890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 53990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 54090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetachedFromRecyclerView(RecyclerView recyclerView) { 54190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onDetachedFromRecyclerView(recyclerView); 54290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onDetached(recyclerView); 54390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 54490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 54590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 546d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 547d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 548d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 549d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 550d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 551d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 552d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 553619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNotNull(holder.mOwnerRecyclerView); 554619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(position, holder.getAdapterPosition()); 555d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 5562d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")"); 557115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar holder.mBoundItem = item; 558d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 559d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 560619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar @Override 561619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar public void onViewRecycled(TestViewHolder holder) { 562619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar super.onViewRecycled(holder); 563619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final int adapterPosition = holder.getAdapterPosition(); 564619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final boolean shouldHavePosition = !holder.isRemoved() && holder.isBound() && 565619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar !holder.isAdapterPositionUnknown() && !holder.isInvalid(); 566619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar String log = "Position check for " + holder.toString(); 567619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(log, shouldHavePosition, adapterPosition != RecyclerView.NO_POSITION); 568619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (shouldHavePosition) { 569619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertTrue(log, mItems.size() > adapterPosition); 570619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertSame(log, holder.mBoundItem, mItems.get(adapterPosition)); 571619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 572619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 573619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar 574b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 575b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 576b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 577b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 578b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 579b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 580b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 581b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 582b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 583b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 584b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 585b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 586b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 587b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 5888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 5898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 5908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 592d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 593d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 594c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar @Override 595c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar public long getItemId(int position) { 596c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar return hasStableIds() ? mItems.get(position).mId : super.getItemId(position); 597c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar } 598c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 5992d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 6002d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = start; i < mItems.size(); i++) { 6012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mItems.get(i).mAdapterIndex += offset; 6022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 605e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar /** 606e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param start inclusive 607e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param end exclusive 608e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param offset 609e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar */ 610e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void offsetOriginalIndicesBetween(int start, int end, int offset) { 611e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = start; i < end && i < mItems.size(); i++) { 612e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.get(i).mAdapterIndex += offset; 613e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 614e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 615e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 616b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 617b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 618b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 619b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 620b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 6218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 6228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 623b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 624cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void dispatchDataSetChanged() throws Throwable { 62522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 62622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 62722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 62822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 62922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 63022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 63122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 63222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 633cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changeAndNotify(final int start, final int count) throws Throwable { 6347e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar runTestOnUiThread(new Runnable() { 6357e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 6367e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 6377e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 6387e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 6397e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 6407e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 6417e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 642cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changePositionsAndNotify(final int... positions) throws Throwable { 643cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase runTestOnUiThread(new Runnable() { 644cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase @Override 645cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void run() { 646cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase for (int i = 0; i < positions.length; i += 1) { 647cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase TestAdapter.super.notifyItemRangeChanged(positions[i], 1); 648cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 649cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 650cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase }); 651cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 652cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase 6538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 6548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 6558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 6568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 6578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 6588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 6598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 6608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 661d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 662d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 663d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 664d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 665d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 666d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 6678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 668115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 669115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 670115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 671e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable { 672e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = 0; i < fromToTuples.length; i += 1) { 673e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int[] tuple = fromToTuples[i]; 674e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar moveItem(tuple[0], tuple[1], false); 675e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 676e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 677cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase dispatchDataSetChanged(); 678e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 679e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 680e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 681115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 682115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 683115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 684e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItem(final int from, final int to, final boolean notifyChange) 685e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar throws Throwable { 686e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar runTestOnUiThread(new Runnable() { 687e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 688e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void run() { 689e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item = mItems.remove(from); 690e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.add(to, item); 691e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar offsetOriginalIndices(from, to - 1); 692e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item.mAdapterIndex = to; 693e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 694e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar notifyDataSetChanged(); 695e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 696e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 697e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }); 698e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 699e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 700115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 701115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyItemMoved 702115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 703115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void moveAndNotify(final int from, final int to) throws Throwable { 704115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar runTestOnUiThread(new Runnable() { 705115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar @Override 706115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void run() { 707115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item item = mItems.remove(from); 708115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar mItems.add(to, item); 709115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar offsetOriginalIndices(from, to - 1); 710115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar item.mAdapterIndex = to; 711115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar notifyItemMoved(from, to); 712115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 713115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar }); 714115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 715115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 716115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 717115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 71890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 71990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public ViewAttachDetachCounter getCounter() { 72090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return mAttachmentCounter; 72190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 72290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 7238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 7258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 7268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public AddRemoveRunnable(int[][] startCountTuples) { 7288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 7298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 7328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 7338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 7348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 7358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 7368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 7408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 7418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 7428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 7438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 7448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 7458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 7468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 7512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // offset others 7522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0], tuple[1]); 7538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < tuple[1]; i++) { 7548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.add(tuple[0], new Item(i, "new item " + i)); 7558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 7578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 7602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int count = -tuple[1]; 7612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0] + count, tuple[1]); 7622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < count; i++) { 7638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 7648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar notifyItemRangeRemoved(tuple[0], count); 7668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 7698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 77090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public boolean isMainThread() { 77190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return Looper.myLooper() == Looper.getMainLooper(); 77290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 77390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 7748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 7758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 7768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 7778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 7788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 7798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 7808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 781d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 782c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 783c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar static class TargetTuple { 784c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 785c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mPosition; 786c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 787c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mLayoutDirection; 788c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 789c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar TargetTuple(int position, int layoutDirection) { 790c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mPosition = position; 791c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mLayoutDirection = layoutDirection; 792c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 793c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 794c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar @Override 795c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public String toString() { 796c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return "TargetTuple{" + 797c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar "mPosition=" + mPosition + 798c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ", mLayoutDirection=" + mLayoutDirection + 799c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar '}'; 800c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 801c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 80290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 80390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public interface AttachDetachCountingAdapter { 80490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 80590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter getCounter(); 80690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 80790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 80890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public class ViewAttachDetachCounter { 80990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 81090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Set<RecyclerView.ViewHolder> mAttachedSet = new HashSet<RecyclerView.ViewHolder>(); 81190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 81290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void validateRemaining(RecyclerView recyclerView) { 81390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 81490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i++) { 81590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 81690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view); 81790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("remaining view should be in attached set " + vh, 81890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.contains(vh)); 81990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 82090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("there should not be any views left in attached set", 82190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar childCount, mAttachedSet.size()); 82290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 82390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 82490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetached(RecyclerView.ViewHolder viewHolder) { 82590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 82690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should be in attached set", 82790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.remove(viewHolder)); 82890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 82990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 83090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 83190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 83290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttached(RecyclerView.ViewHolder viewHolder) { 83490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 83590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should not be in attached set", 83690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(viewHolder)); 83790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 83890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 83990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 84090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 84190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 84290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttached(RecyclerView recyclerView) { 84390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar // when a new RV is attached, clear the set and add all view holders 84490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.clear(); 84590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 84690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i ++) { 84790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 84890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(recyclerView.getChildViewHolder(view)); 84990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 85090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 85190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 85290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetached(RecyclerView recyclerView) { 85390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar validateRemaining(recyclerView); 85490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 85590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 8567a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar} 857