BaseRecyclerViewInstrumentationTest.java revision ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23
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; 21d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2; 22d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log; 23d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View; 24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup; 25310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 26d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView; 27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 28d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList; 29d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List; 30d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch; 31d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 3211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 34d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends 35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ActivityInstrumentationTestCase2<TestActivity> { 36d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 37d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase private static final String TAG = "RecyclerViewTest"; 38d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 39b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private boolean mDebug; 40b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 41b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected RecyclerView mRecyclerView; 42d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected AdapterHelper mAdapterHelper; 448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 457e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Throwable mainThreadException; 467e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 47d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 48b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super("android.support.v7.recyclerview", TestActivity.class); 53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 54b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 55b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 567e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void checkForMainThreadException() throws Throwable { 577e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mainThreadException != null) { 587e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar throw mainThreadException; 597e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 607e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 620b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void setAdapter(final RecyclerView.Adapter adapter) throws Throwable { 630b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 650b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 660b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setAdapter(adapter); 670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 680b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 690b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 710b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void swapAdapter(final RecyclerView.Adapter adapter, 720b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar final boolean removeAndRecycleExistingViews) throws Throwable { 730b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 740b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 750b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 760b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 770b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 780b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 790b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 800b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 817e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void postExceptionToInstrumentation(Throwable t) { 827e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mDebug) { 837e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Log.e(TAG, "captured exception on main thread", t); 847e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mainThreadException != null) { 866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar Log.e(TAG, "receiving another main thread exception. dropping.", t); 876e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 886e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mainThreadException = t; 896e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 906e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 917e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mRecyclerView != null && mRecyclerView 927e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar .getLayoutManager() instanceof TestLayoutManager) { 937e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager(); 947e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar // finish all layouts so that we get the correct exception 957e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar while (lm.layoutLatch.getCount() > 0) { 967e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar lm.layoutLatch.countDown(); 977e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 987e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 997e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1007e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 1018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 1028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected void tearDown() throws Exception { 1038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 1048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 1058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 1068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 1078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 1088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 111ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar super.tearDown(); 112ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar 1136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 1146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 1156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Exception e) { 1166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throw e; 1176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 118ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar throw new Exception(throwable); 1196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 122c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public Rect getDecoratedRecyclerViewBounds() { 123c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new Rect( 124c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft(), 125c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop(), 126c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft() + mRecyclerView.getWidth(), 127c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() 128c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 129c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 130c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 131b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 132d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mRecyclerView == null) { 133d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 134d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 135b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = null; 136b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 137b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 138b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 139b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.removeAllViews(); 140b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 141b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 142b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 143b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 144504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar void waitForAnimations(int seconds) throws InterruptedException { 145504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final CountDownLatch latch = new CountDownLatch(2); 146504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar boolean running = mRecyclerView.mItemAnimator 147504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 148504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 149504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void onAnimationsFinished() { 150504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.countDown(); 151504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 152504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 153504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (running) { 154504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.await(seconds, TimeUnit.SECONDS); 155504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 156504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 157504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 158b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 1590b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar setRecyclerView(recyclerView, true); 1600b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1610b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool) 1620b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar throws Throwable { 163b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 1640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (assignDummyPool) { 1650b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 1660b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 1680b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType); 1690b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (viewHolder == null) { 1700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return null; 1710b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1720b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND); 1730b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPosition = 200; 1740b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mOldPosition = 300; 1750b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPreLayoutPosition = 500; 1760b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return viewHolder; 1778da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar } 1788da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar 1790b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1800b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 1810b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar super.putRecycledView(scrap); 1820b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1830b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }; 1840b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 1850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 187b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 188b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 189b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 191b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 192b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 193b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 194b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 195310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar protected FrameLayout getRecyclerViewContainer() { 196310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return getActivity().mContainer; 197310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 198310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 199b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void requestLayoutOnUIThread(final View view) throws Throwable { 200b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 201b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 202b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 203b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar view.requestLayout(); 204b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 205b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 206b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 207b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 208b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void scrollBy(final int dt) throws Throwable { 209b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 210b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 211b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 212b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 213b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(dt, 0); 214b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } else { 215b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(0, dt); 216b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 217b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 218b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 219b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 220d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 221d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 2228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 2238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 2248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 2268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 2278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 2298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 2328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 2338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 2348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 2358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 2378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 2388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 2408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 2418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 2438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 2448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 2468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 248504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 2498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 251d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 252d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 253d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item mBindedItem; 254d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 255d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 256d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 257310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar itemView.setFocusable(true); 258d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 259504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 260504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 261504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public String toString() { 262504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return super.toString() + " item:" + mBindedItem; 263504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 264d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 265d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 266d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestLayoutManager extends RecyclerView.LayoutManager { 267d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 268d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase CountDownLatch layoutLatch; 269d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 270d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 271d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 272d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 273d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 274b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 275b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 276d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 277d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 278e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar getInstrumentation().waitForIdleSync(); 279d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 280d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 281b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 282b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 283b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 284b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 285b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 286b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 287b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 288b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 289b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 290b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 291b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 2928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS); 293b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 294b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 295d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 296d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 297d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 298d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 299d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 300d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 301d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 302d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 303d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 304d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 305d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 306d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 307d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item item = ((TestViewHolder) lp.mViewHolder).mBindedItem; 308b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 309d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 310d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 3128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 3138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 3148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 3158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 316d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 317d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 318d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 319b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 320b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 321b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 322b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 323f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar void layoutRange(RecyclerView.Recycler recycler, int start, int end) { 324fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar assertScrap(recycler); 325b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 326d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 327d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3285ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 329504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int top = 0; 3305ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 331b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 332d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 333d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 334d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 335b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 336b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 337c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar if (!mRecyclerView.mState.isPreLayout()) { 338c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view 339c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar .getLayoutParams(); 340c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar assertFalse("In post layout, getViewForPosition should never return a view " 341c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar + "that is removed", layoutParams != null 342c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar && layoutParams.isItemRemoved()); 343c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 344b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 3458da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar assertEquals("getViewForPosition should return correct position", 3468da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar i, getPosition(view)); 347c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar addView(view); 348b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 349d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 350504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view) 351504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar , top + getDecoratedMeasuredHeight(view)); 352504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar top += view.getMeasuredHeight(); 353d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 354d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 355fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar 356fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar private void assertScrap(RecyclerView.Recycler recycler) { 357e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (mRecyclerView.getAdapter() != null && 358e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar !mRecyclerView.getAdapter().hasStableIds()) { 359e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) { 360e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid()); 361e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 362fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 363fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 364bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 365bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 366bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollHorizontally() { 367bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 368bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 369bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 370bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 371bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollVertically() { 372bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 373bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 374bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 375bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 376bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 377bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 378bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dx; 379bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 380bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 381bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 382bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 383bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 384bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dy; 385bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 386d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 387d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 388d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 38911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 39011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 391d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAdapterIndex; 393d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final String mText; 395d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3962d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Item(int adapterIndex, String text) { 3972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAdapterIndex = adapterIndex; 3982d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mText = text; 399d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 4028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 4038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 4048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 4052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", originalIndex=" + mAdapterIndex + 4062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", text='" + mText + '\'' + 4078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 4088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 409d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 410d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 411d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestAdapter extends RecyclerView.Adapter<TestViewHolder> { 412d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 413d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 414d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 415d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter(int count) { 416d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 417d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = 0; i < count; i++) { 418d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems.add(new Item(i, "Item " + i)); 419d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 420d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 421d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 422d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 423d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 424d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 425d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 426d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 427d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 428d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 429d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 430d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 4312d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")"); 432d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase holder.mBindedItem = item; 433d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 434d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 435b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 436b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 437b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 438b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 439b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 440b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 441b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 442b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 443b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 444b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 445b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 446b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 447b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 448b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 4498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 4508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 4518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 453d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 454d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 455c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar @Override 456c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar public long getItemId(int position) { 457c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar return hasStableIds() ? mItems.get(position).mId : super.getItemId(position); 458c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar } 459c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 4602d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 4612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = start; i < mItems.size(); i++) { 4622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mItems.get(i).mAdapterIndex += offset; 4632d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4652d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 466e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar /** 467e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param start inclusive 468e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param end exclusive 469e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param offset 470e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar */ 471e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void offsetOriginalIndicesBetween(int start, int end, int offset) { 472e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = start; i < end && i < mItems.size(); i++) { 473e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.get(i).mAdapterIndex += offset; 474e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 475e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 476e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 477b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 478b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 479b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 480b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 481b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 4828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 4838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 484b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 485cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void dispatchDataSetChanged() throws Throwable { 48622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 48722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 48822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 48922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 49022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 49122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 49222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 49322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 494cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changeAndNotify(final int start, final int count) throws Throwable { 4957e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar runTestOnUiThread(new Runnable() { 4967e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 4977e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 4987e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 4997e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 5007e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 5017e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 5027e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 503cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changePositionsAndNotify(final int... positions) throws Throwable { 504cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase runTestOnUiThread(new Runnable() { 505cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase @Override 506cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void run() { 507cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase for (int i = 0; i < positions.length; i += 1) { 508cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase TestAdapter.super.notifyItemRangeChanged(positions[i], 1); 509cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 510cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 511cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase }); 512cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 513cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase 5148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 5158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 5168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 5178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 5188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 5198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 5208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 522d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 523d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 524d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 525d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 526d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 527d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 5288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 529e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable { 530e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = 0; i < fromToTuples.length; i += 1) { 531e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int[] tuple = fromToTuples[i]; 532e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar moveItem(tuple[0], tuple[1], false); 533e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 534e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 535cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase dispatchDataSetChanged(); 536e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 537e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 538e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 539e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItem(final int from, final int to, final boolean notifyChange) 540e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar throws Throwable { 541e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar runTestOnUiThread(new Runnable() { 542e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 543e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void run() { 544e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item = mItems.remove(from); 545e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.add(to, item); 546e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar offsetOriginalIndices(from, to - 1); 547e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item.mAdapterIndex = to; 548e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 549e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar notifyDataSetChanged(); 550e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 551e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 552e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }); 553e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 554e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 5558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 5578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 5588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public AddRemoveRunnable(int[][] startCountTuples) { 5608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 5648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 5658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 5668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 5678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 5688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 5738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 5748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 5758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 5768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 5778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 5788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 5832d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // offset others 5842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0], tuple[1]); 5858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < tuple[1]; i++) { 5868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.add(tuple[0], new Item(i, "new item " + i)); 5878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 5898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 5922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int count = -tuple[1]; 5932d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0] + count, tuple[1]); 5942d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < count; i++) { 5958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 5968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5972d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar notifyItemRangeRemoved(tuple[0], count); 5988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 6038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 6048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 6058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 6068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 6078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 6088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 609d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 610c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 611c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar static class TargetTuple { 612c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 613c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mPosition; 614c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 615c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mLayoutDirection; 616c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 617c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar TargetTuple(int position, int layoutDirection) { 618c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mPosition = position; 619c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mLayoutDirection = layoutDirection; 620c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 621c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 622c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar @Override 623c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public String toString() { 624c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return "TargetTuple{" + 625c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar "mPosition=" + mPosition + 626c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ", mLayoutDirection=" + mLayoutDirection + 627c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar '}'; 628c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 629c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 6307a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar} 631