BaseRecyclerViewInstrumentationTest.java revision 90ca3086dbf66ad6bb8840e46ec8524a705e1c18
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; 2090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport android.os.Debug; 218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport android.os.Looper; 22d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2; 23d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log; 24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View; 25d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup; 26310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView; 28d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 29d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList; 3090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.HashMap; 3190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.HashSet; 32d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List; 3390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.Set; 34d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch; 35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 3611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 37d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 38d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends 39d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ActivityInstrumentationTestCase2<TestActivity> { 40d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 41d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase private static final String TAG = "RecyclerViewTest"; 42d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 43b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private boolean mDebug; 44b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 45b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected RecyclerView mRecyclerView; 46d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected AdapterHelper mAdapterHelper; 488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 497e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Throwable mainThreadException; 507e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 51d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 52b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 54b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 55b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super("android.support.v7.recyclerview", TestActivity.class); 57b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 607e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void checkForMainThreadException() throws Throwable { 617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mainThreadException != null) { 627e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar throw mainThreadException; 637e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 647e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 657e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 660b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void setAdapter(final RecyclerView.Adapter adapter) throws Throwable { 670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 680b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 690b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setAdapter(adapter); 710b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 720b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 730b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 740b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 750b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void swapAdapter(final RecyclerView.Adapter adapter, 760b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar final boolean removeAndRecycleExistingViews) throws Throwable { 770b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 780b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 790b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 80824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar try { 81824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 82824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } catch (Throwable t) { 83824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar postExceptionToInstrumentation(t); 84824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } 850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 860b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 87824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar checkForMainThreadException(); 880b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 890b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 907e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void postExceptionToInstrumentation(Throwable t) { 916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mainThreadException != null) { 926e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar Log.e(TAG, "receiving another main thread exception. dropping.", t); 936e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 9490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Log.e(TAG, "captured exception on main thread", t); 956e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mainThreadException = t; 966e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 976e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 987e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mRecyclerView != null && mRecyclerView 997e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar .getLayoutManager() instanceof TestLayoutManager) { 1007e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager(); 1017e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar // finish all layouts so that we get the correct exception 1027e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar while (lm.layoutLatch.getCount() > 0) { 1037e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar lm.layoutLatch.countDown(); 1047e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1057e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1067e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1077e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 1088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 1098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected void tearDown() throws Exception { 1108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 1118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 1128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 1138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 1148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 1158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 118ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar super.tearDown(); 119ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar 1206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 1216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 1226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Exception e) { 1236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throw e; 1246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 125ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar throw new Exception(throwable); 1266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 129c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public Rect getDecoratedRecyclerViewBounds() { 130c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new Rect( 131c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft(), 132c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop(), 133c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft() + mRecyclerView.getWidth(), 134c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop() + mRecyclerView.getHeight() 135c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 136c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 137c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 138b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 139d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mRecyclerView == null) { 140d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 141d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 14290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar if (!isMainThread()) { 14390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar getInstrumentation().waitForIdleSync(); 14490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 145b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 146b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 147b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 14890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); 14990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar if (adapter instanceof AttachDetachCountingAdapter) { 15090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ((AttachDetachCountingAdapter) adapter).getCounter() 15190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar .validateRemaining(mRecyclerView); 15290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 153b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.removeAllViews(); 154b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 155b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 15690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mRecyclerView = null; 157b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 158b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 159504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar void waitForAnimations(int seconds) throws InterruptedException { 160504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final CountDownLatch latch = new CountDownLatch(2); 161504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar boolean running = mRecyclerView.mItemAnimator 162504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 163504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 164504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void onAnimationsFinished() { 165504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.countDown(); 166504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 167504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 168504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (running) { 169504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.await(seconds, TimeUnit.SECONDS); 170504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 171504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 172504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 173b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 1740b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar setRecyclerView(recyclerView, true); 1750b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1760b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool) 1770b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar throws Throwable { 178b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 1790b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (assignDummyPool) { 1800b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 1810b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1820b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 1830b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType); 1840b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (viewHolder == null) { 1850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return null; 1860b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1870b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND); 1880b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPosition = 200; 1890b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mOldPosition = 300; 1900b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPreLayoutPosition = 500; 1910b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return viewHolder; 1928da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar } 1938da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar 1940b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1950b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 1960b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar super.putRecycledView(scrap); 1970b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1980b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }; 1990b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 2000b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 202b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 203b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 204b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 205b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 206b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 207b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 208b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 209b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 210310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar protected FrameLayout getRecyclerViewContainer() { 211310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return getActivity().mContainer; 212310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 213310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 214b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void requestLayoutOnUIThread(final View view) throws Throwable { 215b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 216b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 217b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 218b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar view.requestLayout(); 219b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 220b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 221b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 222b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 223b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void scrollBy(final int dt) throws Throwable { 224b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 225b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 226b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 227b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 228b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(dt, 0); 229b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } else { 230b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView.scrollBy(0, dt); 231b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 232b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 233b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 234b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 235d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 236d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 2378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 2388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 2398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 2418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 2428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 2448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 2468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 2478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 2488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 2498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 2508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 2518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 2528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 2538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 2558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 2568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 2578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 2588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 2598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 2618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 263504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 2648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 2658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 266d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 267d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 268d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item mBindedItem; 269d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 270d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 271d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 272310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar itemView.setFocusable(true); 273d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 274504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 275504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 276504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public String toString() { 277504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return super.toString() + " item:" + mBindedItem; 278504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 279d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 280d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 281d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestLayoutManager extends RecyclerView.LayoutManager { 282d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 283d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase CountDownLatch layoutLatch; 284d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 285d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 286d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 287d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 288d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 289b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable { 290b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 291d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 292d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 293e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar getInstrumentation().waitForIdleSync(); 294d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 295d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 296b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 297b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 298b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 299b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 300b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 301b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 302b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 303b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 304b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 305b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 306b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 3078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS); 308b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 309b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 310d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 311d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 312d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 313d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 314d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 315d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 316d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 317d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 318d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 319d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 320d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 321d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 322d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Item item = ((TestViewHolder) lp.mViewHolder).mBindedItem; 323b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 324d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 325d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 3278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 3288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 3298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 3308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 331d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 332d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 333d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 334b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 335b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 336b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 337b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 338f485be9def4c0f72cfdfa6b0c616c23f04652817Yigit Boyar void layoutRange(RecyclerView.Recycler recycler, int start, int end) { 339fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar assertScrap(recycler); 340b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 341d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 342d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 3435ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 344504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int top = 0; 3455ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 346b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 347d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 348d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 349d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 350b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 351b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 352c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar if (!mRecyclerView.mState.isPreLayout()) { 353c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view 354c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar .getLayoutParams(); 355c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar assertFalse("In post layout, getViewForPosition should never return a view " 356c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar + "that is removed", layoutParams != null 357c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar && layoutParams.isItemRemoved()); 358c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 359b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 3608da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar assertEquals("getViewForPosition should return correct position", 3618da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar i, getPosition(view)); 362c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar addView(view); 363b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 364d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 365504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view) 366504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar , top + getDecoratedMeasuredHeight(view)); 367504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar top += view.getMeasuredHeight(); 368d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 369d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 370fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar 371fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar private void assertScrap(RecyclerView.Recycler recycler) { 372e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (mRecyclerView.getAdapter() != null && 373e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar !mRecyclerView.getAdapter().hasStableIds()) { 374e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) { 375e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid()); 376e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 377fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 378fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 379bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 380bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 381bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollHorizontally() { 382bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 383bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 384bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 385bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 386bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollVertically() { 387bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 388bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 389bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 390bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 391bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 392bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 393bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dx; 394bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 395bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 396bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 397bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 398bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 399bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dy; 400bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 401d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 402d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 403d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 40411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 40511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 406d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 4072d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAdapterIndex; 408d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 4092d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final String mText; 410d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 4112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Item(int adapterIndex, String text) { 4122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAdapterIndex = adapterIndex; 4132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mText = text; 414d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 4168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 4178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 4188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 4198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 4202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", originalIndex=" + mAdapterIndex + 4212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", text='" + mText + '\'' + 4228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 4238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 424d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 425d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 42690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar class TestAdapter extends RecyclerView.Adapter<TestViewHolder> 42790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar implements AttachDetachCountingAdapter { 428d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 42990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter mAttachmentCounter = new ViewAttachDetachCounter(); 430d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 431d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 432d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter(int count) { 433d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 434d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase for (int i = 0; i < count; i++) { 435d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems.add(new Item(i, "Item " + i)); 436d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 437d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 438d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 439d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 44090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttachedToWindow(TestViewHolder holder) { 44190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewAttachedToWindow(holder); 44290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewAttached(holder); 44390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 44490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 44590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 44690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetachedFromWindow(TestViewHolder holder) { 44790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewDetachedFromWindow(holder); 44890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewDetached(holder); 44990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 45090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 45190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 45290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttachedToRecyclerView(RecyclerView recyclerView) { 45390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onAttachedToRecyclerView(recyclerView); 45490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onAttached(recyclerView); 45590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 45690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 45790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 45890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetachedFromRecyclerView(RecyclerView recyclerView) { 45990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onDetachedFromRecyclerView(recyclerView); 46090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onDetached(recyclerView); 46190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 46290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 46390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 464d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 465d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 466d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 467d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 468d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 469d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 470d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 471d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 4722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")"); 473d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase holder.mBindedItem = item; 474d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 475d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 476b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 477b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 478b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 479b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 480b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 481b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 482b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 483b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 484b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 485b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 486b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 487b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 488b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 489b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 4908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 4918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 4928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 4938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 494d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 495d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 496c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar @Override 497c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar public long getItemId(int position) { 498c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar return hasStableIds() ? mItems.get(position).mId : super.getItemId(position); 499c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar } 500c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 5012d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 5022d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = start; i < mItems.size(); i++) { 5032d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mItems.get(i).mAdapterIndex += offset; 5042d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 5062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 507e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar /** 508e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param start inclusive 509e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param end exclusive 510e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param offset 511e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar */ 512e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void offsetOriginalIndicesBetween(int start, int end, int offset) { 513e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = start; i < end && i < mItems.size(); i++) { 514e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.get(i).mAdapterIndex += offset; 515e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 516e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 517e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 518b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 519b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 520b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 521b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 522b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 5248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 525b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 526cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void dispatchDataSetChanged() throws Throwable { 52722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 52822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 52922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 53022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 53122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 53222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 53322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 53422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 535cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changeAndNotify(final int start, final int count) throws Throwable { 5367e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar runTestOnUiThread(new Runnable() { 5377e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 5387e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 5397e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 5407e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 5417e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 5427e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 5437e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 544cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changePositionsAndNotify(final int... positions) throws Throwable { 545cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase runTestOnUiThread(new Runnable() { 546cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase @Override 547cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void run() { 548cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase for (int i = 0; i < positions.length; i += 1) { 549cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase TestAdapter.super.notifyItemRangeChanged(positions[i], 1); 550cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 551cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 552cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase }); 553cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 554cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase 5558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 5568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 5578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 5588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 5598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 5608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 563d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 564d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 565d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 566d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 567d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 568d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 5698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 570e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable { 571e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = 0; i < fromToTuples.length; i += 1) { 572e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int[] tuple = fromToTuples[i]; 573e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar moveItem(tuple[0], tuple[1], false); 574e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 575e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 576cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase dispatchDataSetChanged(); 577e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 578e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 579e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 580e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItem(final int from, final int to, final boolean notifyChange) 581e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar throws Throwable { 582e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar runTestOnUiThread(new Runnable() { 583e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 584e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void run() { 585e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar Item item = mItems.remove(from); 586e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.add(to, item); 587e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar offsetOriginalIndices(from, to - 1); 588e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar item.mAdapterIndex = to; 589e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 590e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar notifyDataSetChanged(); 591e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 592e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 593e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }); 594e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 595e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 59690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 59790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public ViewAttachDetachCounter getCounter() { 59890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return mAttachmentCounter; 59990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 60090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 6018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 6038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 6048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public AddRemoveRunnable(int[][] startCountTuples) { 6068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 6078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 6108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 6118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 6128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 6138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 6148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 6188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 6198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 6208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 6218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 6228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 6238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 6248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 6292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // offset others 6302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0], tuple[1]); 6318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int i = 0; i < tuple[1]; i++) { 6328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.add(tuple[0], new Item(i, "new item " + i)); 6338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 6358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 6378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 6382d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int count = -tuple[1]; 6392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0] + count, tuple[1]); 6402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < count; i++) { 6418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 6428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6432d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar notifyItemRangeRemoved(tuple[0], count); 6448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 64890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public boolean isMainThread() { 64990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return Looper.myLooper() == Looper.getMainLooper(); 65090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 65190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 6528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 6538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 6548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 6558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 6568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 6578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 6588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 659d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 660c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 661c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar static class TargetTuple { 662c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 663c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mPosition; 664c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 665c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mLayoutDirection; 666c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 667c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar TargetTuple(int position, int layoutDirection) { 668c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mPosition = position; 669c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mLayoutDirection = layoutDirection; 670c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 671c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 672c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar @Override 673c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public String toString() { 674c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return "TargetTuple{" + 675c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar "mPosition=" + mPosition + 676c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ", mLayoutDirection=" + mLayoutDirection + 677c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar '}'; 678c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 679c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 68090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 68190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public interface AttachDetachCountingAdapter { 68290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 68390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter getCounter(); 68490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 68590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 68690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public class ViewAttachDetachCounter { 68790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 68890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Set<RecyclerView.ViewHolder> mAttachedSet = new HashSet<RecyclerView.ViewHolder>(); 68990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 69090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void validateRemaining(RecyclerView recyclerView) { 69190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 69290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i++) { 69390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 69490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view); 69590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("remaining view should be in attached set " + vh, 69690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.contains(vh)); 69790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 69890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("there should not be any views left in attached set", 69990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar childCount, mAttachedSet.size()); 70090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 70190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 70290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetached(RecyclerView.ViewHolder viewHolder) { 70390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 70490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should be in attached set", 70590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.remove(viewHolder)); 70690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 70790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 70890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 70990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 71090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 71190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttached(RecyclerView.ViewHolder viewHolder) { 71290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 71390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should not be in attached set", 71490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(viewHolder)); 71590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 71690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 71790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 71890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 71990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 72090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttached(RecyclerView recyclerView) { 72190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar // when a new RV is attached, clear the set and add all view holders 72290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.clear(); 72390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 72490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i ++) { 72590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 72690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(recyclerView.getChildViewHolder(view)); 72790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 72890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 72990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 73090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetached(RecyclerView recyclerView) { 73190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar validateRemaining(recyclerView); 73290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 73390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 7347a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar} 735