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 192bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport android.app.Instrumentation; 20c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport android.graphics.Rect; 21e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyarimport android.os.Handler; 228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport android.os.Looper; 23e2437126f0225135891544315ff72e54c7de0f59Yigit Boyarimport android.support.v4.view.ViewCompat; 24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2; 25d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log; 26e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.LayoutInflater; 27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View; 28d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup; 29310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 30d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView; 31d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 322bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.InvocationTargetException; 332bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.Method; 34d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList; 3590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.HashSet; 36d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List; 3790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.Set; 38d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch; 39d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 4011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 41da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyarimport java.util.concurrent.locks.ReentrantLock; 42e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.recyclerview.test.R; 43d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 44d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends 45d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ActivityInstrumentationTestCase2<TestActivity> { 46d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 47d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase private static final String TAG = "RecyclerViewTest"; 48d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private boolean mDebug; 50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected RecyclerView mRecyclerView; 52d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected AdapterHelper mAdapterHelper; 548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 557e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar Throwable mainThreadException; 567e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 57e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar Thread mInstrumentationThread; 58e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 59d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 61b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 62b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 63b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super("android.support.v7.recyclerview", TestActivity.class); 65b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 66b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 67b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 687e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void checkForMainThreadException() throws Throwable { 697e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mainThreadException != null) { 707e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar throw mainThreadException; 717e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 727e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 737e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 74e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar @Override 75e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar protected void setUp() throws Exception { 76e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar super.setUp(); 77e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mInstrumentationThread = Thread.currentThread(); 78e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 79e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 80e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar void setHasTransientState(final View view, final boolean value) { 81e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 82e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 83e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 84e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 85e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar ViewCompat.setHasTransientState(view, value); 86e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 87e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 88e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 89e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 90e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 91e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 92e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar 932bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar protected void enableAccessibility() 942bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 952bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar Method getUIAutomation = Instrumentation.class.getMethod("getUiAutomation"); 962bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar getUIAutomation.invoke(getInstrumentation()); 972bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar } 982bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar 990b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void setAdapter(final RecyclerView.Adapter adapter) throws Throwable { 1000b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 1010b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1020b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 1030b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setAdapter(adapter); 1040b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1050b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 1060b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1070b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 108e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected WrappedRecyclerView inflateWrappedRV() { 109e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return (WrappedRecyclerView) 110e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar LayoutInflater.from(getActivity()).inflate(R.layout.wrapped_test_rv, 111e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getRecyclerViewContainer(), false); 112e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 113e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1140b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void swapAdapter(final RecyclerView.Adapter adapter, 1150b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar final boolean removeAndRecycleExistingViews) throws Throwable { 1160b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar runTestOnUiThread(new Runnable() { 1170b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1180b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 119824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar try { 120824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 121824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } catch (Throwable t) { 122824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar postExceptionToInstrumentation(t); 123824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } 1240b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1250b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 126824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar checkForMainThreadException(); 1270b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1280b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 1297e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void postExceptionToInstrumentation(Throwable t) { 130e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (mInstrumentationThread == Thread.currentThread()) { 131e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar throw new RuntimeException(t); 132e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 1336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mainThreadException != null) { 1346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar Log.e(TAG, "receiving another main thread exception. dropping.", t); 1356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 13690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Log.e(TAG, "captured exception on main thread", t); 1376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mainThreadException = t; 1386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1407e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mRecyclerView != null && mRecyclerView 1417e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar .getLayoutManager() instanceof TestLayoutManager) { 1427e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager(); 1437e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar // finish all layouts so that we get the correct exception 1447e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar while (lm.layoutLatch.getCount() > 0) { 1457e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar lm.layoutLatch.countDown(); 1467e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1477e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1487e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1497e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 1508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 1518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected void tearDown() throws Exception { 1528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 1538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 1548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 1558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 1568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 1578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 160ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar super.tearDown(); 161ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar 1626e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 1636e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 1646e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Exception e) { 1656e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throw e; 1666e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 167ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar throw new Exception(throwable); 1686e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 1708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 171c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public Rect getDecoratedRecyclerViewBounds() { 172c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new Rect( 173c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft(), 174c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop(), 17529c349ed8863cc15c2580465227c37771ac49576Yigit Boyar mRecyclerView.getWidth() - mRecyclerView.getPaddingRight(), 17629c349ed8863cc15c2580465227c37771ac49576Yigit Boyar mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom() 177c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 178c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 179c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 180b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 181d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mRecyclerView == null) { 182d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 183d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 18490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar if (!isMainThread()) { 18590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar getInstrumentation().waitForIdleSync(); 18690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 187b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 188b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 189b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 190abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar try { 191abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar final RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); 192abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar if (adapter instanceof AttachDetachCountingAdapter) { 193abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar ((AttachDetachCountingAdapter) adapter).getCounter() 194abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar .validateRemaining(mRecyclerView); 195abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } 196abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar getActivity().mContainer.removeAllViews(); 197abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } catch (Throwable t) { 198abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar postExceptionToInstrumentation(t); 19990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 200b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 201b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 20290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mRecyclerView = null; 203b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 204b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar void waitForAnimations(int seconds) throws InterruptedException { 206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final CountDownLatch latch = new CountDownLatch(2); 207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar boolean running = mRecyclerView.mItemAnimator 208504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 209504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 210504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void onAnimationsFinished() { 211504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.countDown(); 212504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 213504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }); 214504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (running) { 2155a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar latch.countDown(); 216504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar latch.await(seconds, TimeUnit.SECONDS); 217504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 218504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 219504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 220542f1260934df280985294eaef1ec8469863281fYigit Boyar public boolean requestFocus(final View view) { 221542f1260934df280985294eaef1ec8469863281fYigit Boyar final boolean[] result = new boolean[1]; 222542f1260934df280985294eaef1ec8469863281fYigit Boyar getActivity().runOnUiThread(new Runnable() { 223542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 224542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 225542f1260934df280985294eaef1ec8469863281fYigit Boyar result[0] = view.requestFocus(); 226542f1260934df280985294eaef1ec8469863281fYigit Boyar } 227542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 228542f1260934df280985294eaef1ec8469863281fYigit Boyar return result[0]; 229542f1260934df280985294eaef1ec8469863281fYigit Boyar } 230542f1260934df280985294eaef1ec8469863281fYigit Boyar 231b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 2320b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar setRecyclerView(recyclerView, true); 2330b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool) 2350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar throws Throwable { 236f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes setRecyclerView(recyclerView, assignDummyPool, true); 237e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 238e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool, 239e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar boolean addPositionCheckItemAnimator) 240e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar throws Throwable { 241b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 2420b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (assignDummyPool) { 2430b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 2440b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 2450b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 2460b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType); 2470b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (viewHolder == null) { 2480b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return null; 2490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND); 2510b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPosition = 200; 2520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mOldPosition = 300; 2530b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPreLayoutPosition = 500; 2540b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return viewHolder; 2558da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar } 2568da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar 2570b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 2580b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 259619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNull(scrap.mOwnerRecyclerView); 2600b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar super.putRecycledView(scrap); 2610b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 2620b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }; 2630b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 2640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 265e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (addPositionCheckItemAnimator) { 266e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 267e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar @Override 268e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 269e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.State state) { 270e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.ViewHolder vh = parent.getChildViewHolder(view); 271e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (!vh.isRemoved()) { 272e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar assertNotSame("If getItemOffsets is called, child should have a valid" 273619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar + " adapter position unless it is removed : " + vh, 274e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar vh.getAdapterPosition(), RecyclerView.NO_POSITION); 275e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 276e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 277e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar }); 278e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 2798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 280b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar runTestOnUiThread(new Runnable() { 281b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 282b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 283b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar getActivity().mContainer.addView(recyclerView); 284b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 285b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 286b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 287b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 288310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar protected FrameLayout getRecyclerViewContainer() { 289310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return getActivity().mContainer; 290310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 291310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 292e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void requestLayoutOnUIThread(final View view) { 293e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 294e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 295e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 296e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 297e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar view.requestLayout(); 298e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 299e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 300e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 301e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 302e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 303b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 304b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 305e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void scrollBy(final int dt) { 306e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 307e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar runTestOnUiThread(new Runnable() { 308e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 309e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 310e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 311e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar mRecyclerView.scrollBy(dt, 0); 312e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } else { 313e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar mRecyclerView.scrollBy(0, dt); 314e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 315b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 316e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 317e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 318e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 319e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 320e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 321d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 322d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 3238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 3248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 3258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 3288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 3308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 3328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void smoothScrollToPosition(final int position) 3338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 334619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 335619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 336619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 3378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 3388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 3398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 3408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 3418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 343de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar getInstrumentation().waitForIdleSync(); 344de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar Thread.sleep(200); //give scroller some time so start 3458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar while (mRecyclerView.getLayoutManager().isSmoothScrolling() || 3468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 3478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mDebug) { 3488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "SMOOTH scrolling step"); 3498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Thread.sleep(200); 3518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 352619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 353619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 354619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 355504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 3568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 3578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 358ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu void freezeLayout(final boolean freeze) throws Throwable { 359ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu runTestOnUiThread(new Runnable() { 360ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu @Override 361ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu public void run() { 362ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu mRecyclerView.setLayoutFrozen(freeze); 363ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu } 364ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu }); 365ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu } 366ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu 367d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase class TestViewHolder extends RecyclerView.ViewHolder { 368d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 369115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item mBoundItem; 370d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 371d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 372d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 373310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar itemView.setFocusable(true); 374d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 375504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 376504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public String toString() { 378115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar return super.toString() + " item:" + mBoundItem; 379504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 380d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 381da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar class DumbLayoutManager extends TestLayoutManager { 382da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar ReentrantLock mLayoutLock = new ReentrantLock(); 383da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void blockLayout() { 384da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.lock(); 385da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 386d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 387da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void unblockLayout() { 388da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.unlock(); 389da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 390da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar @Override 391da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 392da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.lock(); 393da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar detachAndScrapAttachedViews(recycler); 394da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutRange(recycler, 0, state.getItemCount()); 395da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar if (layoutLatch != null) { 396da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutLatch.countDown(); 397da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 398da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar mLayoutLock.unlock(); 399da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 400da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 401e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public class TestLayoutManager extends RecyclerView.LayoutManager { 402f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes int mScrollVerticallyAmount; 403f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes int mScrollHorizontallyAmount; 404e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected CountDownLatch layoutLatch; 405d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 406d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 407d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 408d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 409d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 410629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit, boolean waitForIdle) 411629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar throws Throwable { 412b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit); 413d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase assertEquals("all expected layouts should be executed at the expected time", 414d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 0, layoutLatch.getCount()); 415629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar if (waitForIdle) { 416629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar getInstrumentation().waitForIdleSync(); 417629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar } 418629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar } 419629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar 420629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar public void waitForLayout(long timeout, TimeUnit timeUnit) 421629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar throws Throwable { 422629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar waitForLayout(timeout, timeUnit, true); 423d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 424d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 425b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 426b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 427b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 428b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 429b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 430b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 431b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 432b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 433b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 434b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 435b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void waitForLayout(long timeout) throws Throwable { 436629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS, true); 437629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar } 438629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar 439629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar public void waitForLayout(long timeout, boolean waitForIdle) throws Throwable { 440629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS, waitForIdle); 441b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 442b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 443d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 444d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 445d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 446d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 447d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 448d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 449d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 450d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 451d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 452d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 453d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 454d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 455115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item item = ((TestViewHolder) lp.mViewHolder).mBoundItem; 456b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 457d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 458d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 4608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 4618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 4628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 4638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 464d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 465d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 466d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 467b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 468b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 469b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 470b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 471e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected void layoutRange(RecyclerView.Recycler recycler, int start, int end) { 472fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar assertScrap(recycler); 473b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 474d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 475d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 4765ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 477504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int top = 0; 4785ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 479b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 480d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 481d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 482d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 483b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 484b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 485c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar if (!mRecyclerView.mState.isPreLayout()) { 486c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view 487c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar .getLayoutParams(); 488c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar assertFalse("In post layout, getViewForPosition should never return a view " 489c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar + "that is removed", layoutParams != null 490c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar && layoutParams.isItemRemoved()); 491c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 492b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 4938da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar assertEquals("getViewForPosition should return correct position", 4948da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar i, getPosition(view)); 495c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar addView(view); 496b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 497d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 498e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) { 499e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar layoutDecorated(view, getWidth() - getDecoratedMeasuredWidth(view), top, 500e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getWidth(), top + getDecoratedMeasuredHeight(view)); 501e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 502e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view) 503e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar , top + getDecoratedMeasuredHeight(view)); 504e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 505e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 506504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar top += view.getMeasuredHeight(); 507d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 508d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 509fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar 510fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar private void assertScrap(RecyclerView.Recycler recycler) { 511e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (mRecyclerView.getAdapter() != null && 512e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar !mRecyclerView.getAdapter().hasStableIds()) { 513e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) { 514e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid()); 515e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 516fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 517fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 518bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 519bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 520bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollHorizontally() { 521bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 522bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 523bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 524bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 525bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollVertically() { 526bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 527bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 528bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 529bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 530bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 531bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 532f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes mScrollHorizontallyAmount += dx; 533bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dx; 534bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 535bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 536bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 537bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 538bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 539f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes mScrollVerticallyAmount += dy; 540bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dy; 541bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 542d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 543d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 544d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 54511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 54611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 547d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAdapterIndex; 549d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5502d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final String mText; 551d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5522d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Item(int adapterIndex, String text) { 5532d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAdapterIndex = adapterIndex; 5542d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mText = text; 555d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 5568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 5578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 5588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 5598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 5608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 5612d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", originalIndex=" + mAdapterIndex + 5622d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", text='" + mText + '\'' + 5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 5648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 565d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 566d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 567e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public class TestAdapter extends RecyclerView.Adapter<TestViewHolder> 56890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar implements AttachDetachCountingAdapter { 569d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 5703d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public static final String DEFAULT_ITEM_PREFIX = "Item "; 5713d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 57290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter mAttachmentCounter = new ViewAttachDetachCounter(); 573d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 574d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 575e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public TestAdapter(int count) { 576d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 5773d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev addItems(0, count, DEFAULT_ITEM_PREFIX); 5783d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 5793d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 5803d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev private void addItems(int pos, int count, String prefix) { 5813d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev for (int i = 0; i < count; i++, pos++) { 5823d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mItems.add(pos, new Item(pos, prefix)); 583d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 584d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 585d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 586d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 58790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttachedToWindow(TestViewHolder holder) { 58890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewAttachedToWindow(holder); 58990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewAttached(holder); 59090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 59190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 59290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 59390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetachedFromWindow(TestViewHolder holder) { 59490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewDetachedFromWindow(holder); 59590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewDetached(holder); 59690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 59790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 59890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 59990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttachedToRecyclerView(RecyclerView recyclerView) { 60090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onAttachedToRecyclerView(recyclerView); 60190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onAttached(recyclerView); 60290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 60390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 60490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 60590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetachedFromRecyclerView(RecyclerView recyclerView) { 60690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onDetachedFromRecyclerView(recyclerView); 60790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onDetached(recyclerView); 60890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 60990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 61090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 611d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 612d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 613d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new TestViewHolder(new TextView(parent.getContext())); 614d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 615d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 616d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 617d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 618619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNotNull(holder.mOwnerRecyclerView); 619619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(position, holder.getAdapterPosition()); 620d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 6212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")"); 622115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar holder.mBoundItem = item; 623d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 624d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 625888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar public Item getItemAt(int position) { 626888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return mItems.get(position); 627888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 628888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 629619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar @Override 630619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar public void onViewRecycled(TestViewHolder holder) { 631619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar super.onViewRecycled(holder); 632619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final int adapterPosition = holder.getAdapterPosition(); 633619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final boolean shouldHavePosition = !holder.isRemoved() && holder.isBound() && 634619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar !holder.isAdapterPositionUnknown() && !holder.isInvalid(); 635619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar String log = "Position check for " + holder.toString(); 636619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(log, shouldHavePosition, adapterPosition != RecyclerView.NO_POSITION); 637619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (shouldHavePosition) { 638619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertTrue(log, mItems.size() > adapterPosition); 639619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertSame(log, holder.mBoundItem, mItems.get(adapterPosition)); 640619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 641619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 642619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar 643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 644b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 645b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 646b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 647b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 648b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 649b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 650b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 651b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 652b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 653b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 654b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 655b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 656b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 6578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 6588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 6598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 6608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 661d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 662d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 663c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar @Override 664c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar public long getItemId(int position) { 665c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar return hasStableIds() ? mItems.get(position).mId : super.getItemId(position); 666c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar } 667c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 6682d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 6692d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = start; i < mItems.size(); i++) { 6702d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mItems.get(i).mAdapterIndex += offset; 6712d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 6732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 674e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar /** 675e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param start inclusive 676e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param end exclusive 677e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param offset 678e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar */ 679e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void offsetOriginalIndicesBetween(int start, int end, int offset) { 680e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = start; i < end && i < mItems.size(); i++) { 681e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.get(i).mAdapterIndex += offset; 682e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 683e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 684e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 6853d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void addAndNotify(final int count) throws Throwable { 6863d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertEquals(0, mItems.size()); 6873d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev new AddRemoveRunnable(DEFAULT_ITEM_PREFIX, new int[]{0, count}).runOnMainThread(); 6883d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 6893d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 690b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 691b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 692b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 693b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 694b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 6958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 6968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 697b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 698cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void dispatchDataSetChanged() throws Throwable { 69922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar runTestOnUiThread(new Runnable() { 70022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 70122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 70222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 70322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 70422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 70522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 70622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 707cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changeAndNotify(final int start, final int count) throws Throwable { 7087e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar runTestOnUiThread(new Runnable() { 7097e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 7107e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 7117e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 7127e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 7137e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 7147e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 7157e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 716cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changePositionsAndNotify(final int... positions) throws Throwable { 717cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase runTestOnUiThread(new Runnable() { 718cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase @Override 719cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void run() { 720cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase for (int i = 0; i < positions.length; i += 1) { 721cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase TestAdapter.super.notifyItemRangeChanged(positions[i], 1); 722cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 723cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 724cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase }); 725cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 726cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase 7278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 7288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 7298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 7308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 7318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 7328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 7338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 7348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar new AddRemoveRunnable(startCountTuples).runOnMainThread(); 735d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 736d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 737d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 738d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 739d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 740d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 7418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 742115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 743115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 744115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 745e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable { 746e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = 0; i < fromToTuples.length; i += 1) { 747e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int[] tuple = fromToTuples[i]; 748e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar moveItem(tuple[0], tuple[1], false); 749e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 750e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 751cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase dispatchDataSetChanged(); 752e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 753e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 754e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 755115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 756115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 757115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 758e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItem(final int from, final int to, final boolean notifyChange) 759e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar throws Throwable { 760e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar runTestOnUiThread(new Runnable() { 761e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 762e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void run() { 763ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar moveInUIThread(from, to); 764e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 765e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar notifyDataSetChanged(); 766e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 767e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 768e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }); 769e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 770e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 771115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 772115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyItemMoved 773115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 774115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void moveAndNotify(final int from, final int to) throws Throwable { 775115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar runTestOnUiThread(new Runnable() { 776115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar @Override 777115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void run() { 778ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar moveInUIThread(from, to); 779115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar notifyItemMoved(from, to); 780115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 781115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar }); 782115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 783115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 7843d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void clearOnUIThread() { 7853d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertEquals("clearOnUIThread called from a wrong thread", 7863d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev Looper.getMainLooper(), Looper.myLooper()); 7873d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mItems = new ArrayList<Item>(); 7883d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev notifyDataSetChanged(); 7893d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 7903d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 791ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar protected void moveInUIThread(int from, int to) { 792ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar Item item = mItems.remove(from); 793ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar offsetOriginalIndices(from, -1); 794ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar mItems.add(to, item); 795ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar offsetOriginalIndices(to + 1, 1); 796ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar item.mAdapterIndex = to; 797ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar } 798115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 799115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 80090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 80190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public ViewAttachDetachCounter getCounter() { 80290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return mAttachmentCounter; 80390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 80490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 8058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 8073d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev final String mNewItemPrefix; 8088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 8098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8103d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public AddRemoveRunnable(String newItemPrefix, int[]... startCountTuples) { 8113d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mNewItemPrefix = newItemPrefix; 8128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 8138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8153d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public AddRemoveRunnable(int[][] startCountTuples) { 8163d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev this("new item ", startCountTuples); 8173d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 8183d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 8198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runOnMainThread() throws Throwable { 8208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 8218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar run(); 8228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 8238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar runTestOnUiThread(this); 8248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 8288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 8298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 8308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 8318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 8328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 8338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 8348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 8392d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // offset others 8402d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0], tuple[1]); 8413d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev addItems(tuple[0], tuple[1], mNewItemPrefix); 8428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 8438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 8458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 8462d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int count = -tuple[1]; 8472d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0] + count, tuple[1]); 8482d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < count; i++) { 8498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 8508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8512d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar notifyItemRangeRemoved(tuple[0], count); 8528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 8558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 85690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public boolean isMainThread() { 85790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return Looper.myLooper() == Looper.getMainLooper(); 85890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 85990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 8608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 8618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void runTestOnUiThread(Runnable r) throws Throwable { 8628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (Looper.myLooper() == Looper.getMainLooper()) { 8638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar r.run(); 8648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 8658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar super.runTestOnUiThread(r); 8668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 867d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 868c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 869c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar static class TargetTuple { 870c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 871c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mPosition; 872c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 873c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mLayoutDirection; 874c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 875c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar TargetTuple(int position, int layoutDirection) { 876c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mPosition = position; 877c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mLayoutDirection = layoutDirection; 878c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 879c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 880c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar @Override 881c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public String toString() { 882c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return "TargetTuple{" + 883c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar "mPosition=" + mPosition + 884c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ", mLayoutDirection=" + mLayoutDirection + 885c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar '}'; 886c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 887c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 88890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 88990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public interface AttachDetachCountingAdapter { 89090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 89190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter getCounter(); 89290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 89390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 89490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public class ViewAttachDetachCounter { 89590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 89690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Set<RecyclerView.ViewHolder> mAttachedSet = new HashSet<RecyclerView.ViewHolder>(); 89790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 89890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void validateRemaining(RecyclerView recyclerView) { 89990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 90090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i++) { 90190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 90290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view); 90390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("remaining view should be in attached set " + vh, 90490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.contains(vh)); 90590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 90690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("there should not be any views left in attached set", 90790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar childCount, mAttachedSet.size()); 90890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 90990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 91090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetached(RecyclerView.ViewHolder viewHolder) { 91190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 91290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should be in attached set", 91390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.remove(viewHolder)); 91490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 91590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 91690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 91790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 91890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 91990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttached(RecyclerView.ViewHolder viewHolder) { 92090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 92190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should not be in attached set", 92290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(viewHolder)); 92390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 92490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 92590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 92690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 92790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 92890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttached(RecyclerView recyclerView) { 92990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar // when a new RV is attached, clear the set and add all view holders 93090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.clear(); 93190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 93290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i ++) { 93390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 93490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(recyclerView.getChildViewHolder(view)); 93590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 93690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 93790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 93890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetached(RecyclerView recyclerView) { 93990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar validateRemaining(recyclerView); 94090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 94190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 9427a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar} 943