BaseRecyclerViewInstrumentationTest.java revision c50c4cad31d73e574b27bb3d7581542975e37263
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(); 1116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 1126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 1136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Exception e) { 1146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throw e; 1156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 1166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throwable.printStackTrace(); 1176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.tearDown(); 1198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 121c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public Rect getDecoratedRecyclerViewBounds() { 122c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new Rect( 123c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft(), 124c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop(), 125c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft() + mRecyclerView.getWidth(), 126c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() 127c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 128c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 129c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 130b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 131b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = null; 132b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 133b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 134b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 135b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.removeAllViews(); 136b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 137b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 138b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 139b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 140504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar void waitForAnimations(int seconds) throws InterruptedException { 141504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final CountDownLatch latch = new CountDownLatch(2); 142504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar boolean running = mRecyclerView.mItemAnimator 143504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 144504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 145504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void onAnimationsFinished() { 146504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.countDown(); 147504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 148504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 149504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (running) { 150504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.await(seconds, TimeUnit.SECONDS); 151504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 152504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 153504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 154b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 1550b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar setRecyclerView(recyclerView, true); 1560b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1570b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool) 1580b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar throws Throwable { 159b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 1600b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (assignDummyPool) { 1610b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 1620b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1630b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 1640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType); 1650b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (viewHolder == null) { 1660b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return null; 1670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1680b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND); 1690b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPosition = 200; 1700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mOldPosition = 300; 1710b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPreLayoutPosition = 500; 1720b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return viewHolder; 1738da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar } 1748da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar 1750b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1760b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 1770b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar super.putRecycledView(scrap); 1780b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1790b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }; 1800b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 1810b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 183b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 184b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 185b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 186b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 187b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 188b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 189b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 191310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar protected FrameLayout getRecyclerViewContainer() { 192310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return getActivity().mContainer; 193310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 194310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 195b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void requestLayoutOnUIThread(final View view) throws Throwable { 196b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 197b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 198b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 199b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar view.requestLayout(); 200b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 201b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 202b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 203b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 204b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void scrollBy(final int dt) throws Throwable { 205b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 206b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 207b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 208b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 209b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(dt, 0); 210b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } else { 211b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(0, dt); 212b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 213b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 214b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 215b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 216d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 217d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 2188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 2198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 2208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 2228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 2238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 2258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 2288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 2298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 2308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 2318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 2338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 2348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 2368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 2378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 2388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 2398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 2408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 244504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 2458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 247d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 248d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 249d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item mBindedItem; 250d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 251d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 252d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 253310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar itemView.setFocusable(true); 254d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 255504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 256504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 257504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public String toString() { 258504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return super.toString() + " item:" + mBindedItem; 259504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 260d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 261d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 262d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestLayoutManager extends RecyclerView.LayoutManager { 263d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 264d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase CountDownLatch layoutLatch; 265d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 266d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 267d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 268d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 269d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 270b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 271b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 272d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 273d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 274e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar getInstrumentation().waitForIdleSync(); 275d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 276d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 277b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 278b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 279b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 280b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 281b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 282b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 283b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 284b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 285b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 286b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 287b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 2888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS); 289b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 290b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 291d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 292d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 293d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 294d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 295d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 296d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 297d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 298d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 299d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 300d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 301d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 302d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 303d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item item = ((TestViewHolder) lp.mViewHolder).mBindedItem; 304b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 305d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 306d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 3088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 3098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 3108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 3118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 312d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 313d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 314d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 315b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 316b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 317b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 318b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 319f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar void layoutRange(RecyclerView.Recycler recycler, int start, int end) { 320fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar assertScrap(recycler); 321b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 322d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 323d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3245ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 325504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int top = 0; 3265ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 327b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 328d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 329d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 330d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 331b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 332b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 333c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar if (!mRecyclerView.mState.isPreLayout()) { 334c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view 335c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar .getLayoutParams(); 336c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar assertFalse("In post layout, getViewForPosition should never return a view " 337c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar + "that is removed", layoutParams != null 338c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar && layoutParams.isItemRemoved()); 339c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 340b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 3418da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar assertEquals("getViewForPosition should return correct position", 3428da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar i, getPosition(view)); 343c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar addView(view); 344b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 345d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 346504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view) 347504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar , top + getDecoratedMeasuredHeight(view)); 348504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar top += view.getMeasuredHeight(); 349d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 350d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 351fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar 352fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar private void assertScrap(RecyclerView.Recycler recycler) { 353e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (mRecyclerView.getAdapter() != null && 354e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar !mRecyclerView.getAdapter().hasStableIds()) { 355e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) { 356e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid()); 357e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 358fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 359fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 360d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 361d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 362d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 36311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 36411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 365d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAdapterIndex; 367d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final String mText; 369d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Item(int adapterIndex, String text) { 3712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAdapterIndex = adapterIndex; 3722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mText = text; 373d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 3758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 3768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 3778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 3788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 3792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", originalIndex=" + mAdapterIndex + 3802d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", text='" + mText + '\'' + 3818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 3828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 383d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 384d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 385d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestAdapter extends RecyclerView.Adapter<TestViewHolder> { 386d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 387d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 388d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 389d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter(int count) { 390d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 391d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = 0; i < count; i++) { 392d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems.add(new Item(i, "Item " + i)); 393d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 394d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 395d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 396d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 397d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 398d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 399d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 400d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 401d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 402d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 403d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 404d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 4052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")"); 406d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase holder.mBindedItem = item; 407d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 408d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 409b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 410b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 411b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 412b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 413b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 414b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 415b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 416b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 417b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 418b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 419b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 420b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 421b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 422b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 4238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 4248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 4258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 427d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 428d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 429c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar @Override 430c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar public long getItemId(int position) { 431c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar return hasStableIds() ? mItems.get(position).mId : super.getItemId(position); 432c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar } 433c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 4342d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 4352d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = start; i < mItems.size(); i++) { 4362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mItems.get(i).mAdapterIndex += offset; 4372d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 4392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 440e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar /** 441e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param start inclusive 442e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param end exclusive 443e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param offset 444e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar */ 445e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void offsetOriginalIndicesBetween(int start, int end, int offset) { 446e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = start; i < end && i < mItems.size(); i++) { 447e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.get(i).mAdapterIndex += offset; 448e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 449e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 450e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 451b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 452b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 453b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 454b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 455b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 4568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 4578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 458b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 459cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void dispatchDataSetChanged() throws Throwable { 46022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 46122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 46222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 46322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 46422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 46522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 46622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 46722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 468cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changeAndNotify(final int start, final int count) throws Throwable { 4697e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar runTestOnUiThread(new Runnable() { 4707e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 4717e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 4727e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 4737e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 4747e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 4757e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 4767e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 477cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changePositionsAndNotify(final int... positions) throws Throwable { 478cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase runTestOnUiThread(new Runnable() { 479cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase @Override 480cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void run() { 481cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase for (int i = 0; i < positions.length; i += 1) { 482cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase TestAdapter.super.notifyItemRangeChanged(positions[i], 1); 483cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 484cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 485cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase }); 486cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 487cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase 4888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 4898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 4908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 4918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 4928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 4938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 4948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 4958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 496d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 497d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 498d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 499d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 500d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 501d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 5028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 503e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable { 504e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = 0; i < fromToTuples.length; i += 1) { 505e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int[] tuple = fromToTuples[i]; 506e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar moveItem(tuple[0], tuple[1], false); 507e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 508e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 509cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase dispatchDataSetChanged(); 510e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 511e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 512e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 513e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItem(final int from, final int to, final boolean notifyChange) 514e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar throws Throwable { 515e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar runTestOnUiThread(new Runnable() { 516e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 517e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void run() { 518e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item = mItems.remove(from); 519e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.add(to, item); 520e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar offsetOriginalIndices(from, to - 1); 521e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item.mAdapterIndex = to; 522e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 523e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar notifyDataSetChanged(); 524e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 525e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 526e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }); 527e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 528e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 5298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 5318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 5328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public AddRemoveRunnable(int[][] startCountTuples) { 5348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 5358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 5388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 5398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 5408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 5418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 5428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 5478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 5488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 5498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 5508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 5518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 5528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 5572d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // offset others 5582d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0], tuple[1]); 5598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < tuple[1]; i++) { 5608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.add(tuple[0], new Item(i, "new item " + i)); 5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 5662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int count = -tuple[1]; 5672d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0] + count, tuple[1]); 5682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < count; i++) { 5698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 5708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar notifyItemRangeRemoved(tuple[0], count); 5728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 5758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 5788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 5798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 5808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 5818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 5828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 583d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 584c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 585c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar static class TargetTuple { 586c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 587c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mPosition; 588c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 589c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mLayoutDirection; 590c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 591c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar TargetTuple(int position, int layoutDirection) { 592c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mPosition = position; 593c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mLayoutDirection = layoutDirection; 594c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 595c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 596c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar @Override 597c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public String toString() { 598c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return "TargetTuple{" + 599c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar "mPosition=" + mPosition + 600c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ", mLayoutDirection=" + mLayoutDirection + 601c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar '}'; 602c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 603c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 6047a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar} 605