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 19959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyarimport static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE; 20959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar 2142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertEquals; 2242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertFalse; 2342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertNotNull; 2442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertNotSame; 2542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertNull; 2642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertSame; 2742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertThat; 2842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static org.junit.Assert.assertTrue; 2942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas 3042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport static java.util.concurrent.TimeUnit.SECONDS; 310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 322bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport android.app.Instrumentation; 33c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport android.graphics.Rect; 348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport android.os.Looper; 353911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurskaimport android.support.annotation.Nullable; 360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.test.InstrumentationRegistry; 370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.test.rule.ActivityTestRule; 38e2437126f0225135891544315ff72e54c7de0f59Yigit Boyarimport android.support.v4.view.ViewCompat; 3942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport android.support.v7.recyclerview.test.R; 40959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyarimport android.support.v7.recyclerview.test.SameActivityTestRule; 41d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log; 42e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.LayoutInflater; 43d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View; 44d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup; 45310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 46d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView; 47d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 4842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport org.hamcrest.CoreMatchers; 4942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport org.hamcrest.MatcherAssert; 5042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport org.junit.After; 5142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport org.junit.Before; 5242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport org.junit.Rule; 5342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas 542bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.InvocationTargetException; 552bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.Method; 56d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList; 5790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.HashSet; 58d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List; 5990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.Set; 60d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch; 61d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit; 62959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyarimport java.util.concurrent.atomic.AtomicBoolean; 6311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 6466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarabstract public class BaseRecyclerViewInstrumentationTest { 66d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 67d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase private static final String TAG = "RecyclerViewTest"; 68d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 69b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar private boolean mDebug; 70b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 71b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar protected RecyclerView mRecyclerView; 72d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar protected AdapterHelper mAdapterHelper; 748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar private Throwable mMainThreadException; 7666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 7766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar private boolean mIgnoreMainThreadException = false; 787e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 79e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar Thread mInstrumentationThread; 80e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Rule 82959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public ActivityTestRule<TestActivity> mActivityRule = new SameActivityTestRule() { 83959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 84959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public boolean canReUseActivity() { 85959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar return BaseRecyclerViewInstrumentationTest.this.canReUseActivity(); 86959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 87959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar }; 880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 89d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public BaseRecyclerViewInstrumentationTest() { 90b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar this(false); 91b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 92b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 93b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public BaseRecyclerViewInstrumentationTest(boolean debug) { 94b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mDebug = debug; 95b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 96b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 977e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void checkForMainThreadException() throws Throwable { 9866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar if (!mIgnoreMainThreadException && mMainThreadException != null) { 9966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar throw mMainThreadException; 1007e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1017e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 1027e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 10366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void setIgnoreMainThreadException(boolean ignoreMainThreadException) { 10466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar mIgnoreMainThreadException = ignoreMainThreadException; 10566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar } 10666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 10766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public Throwable getMainThreadException() { 10866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar return mMainThreadException; 10966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar } 11066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar 1110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar protected TestActivity getActivity() { 1120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return mActivityRule.getActivity(); 1130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 1150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Before 1160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public final void setUpInsThread() throws Exception { 117e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar mInstrumentationThread = Thread.currentThread(); 1189f5e74d12c89334c2bd439b1bf19fdc5ebfbc137Aurimas Liutikas Item.idCounter.set(0); 119e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 120e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar 121e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar void setHasTransientState(final View view, final boolean value) { 122e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar try { 12342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 124e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar @Override 125e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar public void run() { 126e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar ViewCompat.setHasTransientState(view, value); 127e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 128e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar }); 129e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } catch (Throwable throwable) { 130e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar Log.e(TAG, "", throwable); 131e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 132e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 133e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar 134959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public boolean canReUseActivity() { 135959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar return true; 136959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 137959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar 1382bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar protected void enableAccessibility() 1392bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 1402bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar Method getUIAutomation = Instrumentation.class.getMethod("getUiAutomation"); 1410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar getUIAutomation.invoke(InstrumentationRegistry.getInstrumentation()); 1422bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar } 1432bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar 1440b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void setAdapter(final RecyclerView.Adapter adapter) throws Throwable { 14542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1460b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1470b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 1480b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setAdapter(adapter); 1490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 1510b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 1539c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri public View focusSearch(final View focused, final int direction) throws Throwable { 1549c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return focusSearch(focused, direction, false); 1559c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 1569c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 1579c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri public View focusSearch(final View focused, final int direction, boolean waitForScroll) 158f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar throws Throwable { 159d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar final View[] result = new View[1]; 16042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 161f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar @Override 162f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar public void run() { 163f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar View view = focused.focusSearch(direction); 164f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar if (view != null && view != focused) { 165f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar view.requestFocus(); 166f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 167d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar result[0] = view; 168f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 169f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar }); 1709c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (waitForScroll && (result[0] != null)) { 1719c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri waitForIdleScroll(mRecyclerView); 1729c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 173d8d42d52f8ae53107ffa849b86496650182e24b8Yigit Boyar return result[0]; 174f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar } 175f89e1b82c74b7b85df3a349340a643f62fc5bfa1Yigit Boyar 176e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected WrappedRecyclerView inflateWrappedRV() { 177e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return (WrappedRecyclerView) 178e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar LayoutInflater.from(getActivity()).inflate(R.layout.wrapped_test_rv, 179e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getRecyclerViewContainer(), false); 180e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 181e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1820b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar void swapAdapter(final RecyclerView.Adapter adapter, 1830b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar final boolean removeAndRecycleExistingViews) throws Throwable { 18442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1850b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 1860b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void run() { 187824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar try { 188824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 189824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } catch (Throwable t) { 190824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar postExceptionToInstrumentation(t); 191824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar } 1920b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1930b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }); 194824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar checkForMainThreadException(); 1950b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 1960b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar 1977e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar void postExceptionToInstrumentation(Throwable t) { 198e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar if (mInstrumentationThread == Thread.currentThread()) { 199e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar throw new RuntimeException(t); 200e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar } 20166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar if (mMainThreadException != null) { 2026e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar Log.e(TAG, "receiving another main thread exception. dropping.", t); 2036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 20490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Log.e(TAG, "captured exception on main thread", t); 20566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar mMainThreadException = t; 2066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 2087e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar if (mRecyclerView != null && mRecyclerView 2097e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar .getLayoutManager() instanceof TestLayoutManager) { 2107e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager(); 2117e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar // finish all layouts so that we get the correct exception 2127f5e94985f795f81884334ca2752a0469a998762Yigit Boyar if (lm.layoutLatch != null) { 2137f5e94985f795f81884334ca2752a0469a998762Yigit Boyar while (lm.layoutLatch.getCount() > 0) { 2147f5e94985f795f81884334ca2752a0469a998762Yigit Boyar lm.layoutLatch.countDown(); 2157f5e94985f795f81884334ca2752a0469a998762Yigit Boyar } 2167e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 2177e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 2187e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 2197e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public Instrumentation getInstrumentation() { 2210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar return InstrumentationRegistry.getInstrumentation(); 2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar } 2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 2240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @After 2250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public final void tearDown() throws Exception { 2268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (mRecyclerView != null) { 2278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar try { 2288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar removeRecyclerView(); 2298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } catch (Throwable throwable) { 2308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar throwable.printStackTrace(); 2318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 2328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 2338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar getInstrumentation().waitForIdleSync(); 234ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar 2356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 2366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 2376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Exception e) { 2386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throw e; 2396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 240121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar throw new Exception(Log.getStackTraceString(throwable)); 2416e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 2428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 2438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 244c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public Rect getDecoratedRecyclerViewBounds() { 245c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new Rect( 246c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingLeft(), 247c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mRecyclerView.getPaddingTop(), 24829c349ed8863cc15c2580465227c37771ac49576Yigit Boyar mRecyclerView.getWidth() - mRecyclerView.getPaddingRight(), 24929c349ed8863cc15c2580465227c37771ac49576Yigit Boyar mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom() 250c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 251c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 252c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 253b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void removeRecyclerView() throws Throwable { 254d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar if (mRecyclerView == null) { 255d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar return; 256d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar } 25790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar if (!isMainThread()) { 25890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar getInstrumentation().waitForIdleSync(); 25990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 26042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 261b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 262b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 263abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar try { 26466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar // do not run validation if we already have an error 26566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar if (mMainThreadException == null) { 26666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar final RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); 26766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar if (adapter instanceof AttachDetachCountingAdapter) { 26866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar ((AttachDetachCountingAdapter) adapter).getCounter() 26966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar .validateRemaining(mRecyclerView); 27066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar } 271abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } 272a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar getActivity().getContainer().removeAllViews(); 273abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar } catch (Throwable t) { 274abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar postExceptionToInstrumentation(t); 27590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 276b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 277b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 27890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mRecyclerView = null; 279b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 280b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 2819051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar void waitForAnimations(int seconds) throws Throwable { 2829051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar final CountDownLatch latch = new CountDownLatch(1); 28342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 2849051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar @Override 2859051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar public void run() { 2869051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar mRecyclerView.mItemAnimator 2879051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 2889051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar @Override 2899051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar public void onAnimationsFinished() { 2909051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar latch.countDown(); 2919051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar } 2929051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar }); 2939051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar } 2949051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar }); 2959051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar 2969051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar assertTrue("animations didn't finish on expected time of " + seconds + " seconds", 2979051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar latch.await(seconds, TimeUnit.SECONDS)); 298504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 299504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 300959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void waitForIdleScroll(final RecyclerView recyclerView) throws Throwable { 301959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar final CountDownLatch latch = new CountDownLatch(1); 30242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 303959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 304959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void run() { 305959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar RecyclerView.OnScrollListener listener = new RecyclerView.OnScrollListener() { 306959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 307959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 308959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar if (newState == SCROLL_STATE_IDLE) { 309959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar latch.countDown(); 310959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar recyclerView.removeOnScrollListener(this); 311959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 312959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 313959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar }; 314959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar if (recyclerView.getScrollState() == SCROLL_STATE_IDLE) { 315959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar latch.countDown(); 316959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } else { 317959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar recyclerView.addOnScrollListener(listener); 318959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 319959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 320959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar }); 321959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar assertTrue("should go idle in 10 seconds", latch.await(10, TimeUnit.SECONDS)); 322959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 323959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar 324959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public boolean requestFocus(final View view, boolean waitForScroll) throws Throwable { 325542f1260934df280985294eaef1ec8469863281fYigit Boyar final boolean[] result = new boolean[1]; 32642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 32742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas @Override 32842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void run() { 32942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas result[0] = view.requestFocus(); 33042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas } 33142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas }); 332959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar if (waitForScroll && result[0]) { 333959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar waitForIdleScroll(mRecyclerView); 334959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 335542f1260934df280985294eaef1ec8469863281fYigit Boyar return result[0]; 336542f1260934df280985294eaef1ec8469863281fYigit Boyar } 337542f1260934df280985294eaef1ec8469863281fYigit Boyar 338b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void setRecyclerView(final RecyclerView recyclerView) throws Throwable { 3390b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar setRecyclerView(recyclerView, true); 3400b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 3410b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool) 3420b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar throws Throwable { 343f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes setRecyclerView(recyclerView, assignDummyPool, true); 344e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 345e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool, 346e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar boolean addPositionCheckItemAnimator) 347e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar throws Throwable { 348b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar mRecyclerView = recyclerView; 3490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (assignDummyPool) { 3500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 3510b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 3520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 3530b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType); 3540b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar if (viewHolder == null) { 3550b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return null; 3560b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 3570b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND); 3580b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPosition = 200; 3590b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mOldPosition = 300; 3600b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar viewHolder.mPreLayoutPosition = 500; 3610b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar return viewHolder; 3628da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar } 3638da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar 3640b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar @Override 3650b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 366619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNull(scrap.mOwnerRecyclerView); 3670b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar super.putRecycledView(scrap); 3680b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 3690b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar }; 3700b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 3710b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar } 372e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (addPositionCheckItemAnimator) { 373e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 374e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar @Override 375e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 376e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.State state) { 377e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar RecyclerView.ViewHolder vh = parent.getChildViewHolder(view); 378e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar if (!vh.isRemoved()) { 379e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar assertNotSame("If getItemOffsets is called, child should have a valid" 3800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar + " adapter position unless it is removed : " + vh, 3810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar vh.getAdapterPosition(), RecyclerView.NO_POSITION); 382e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 383e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 384e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar }); 385e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar } 3868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mAdapterHelper = recyclerView.mAdapterHelper; 38742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 388b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar @Override 389b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void run() { 390a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar getActivity().getContainer().addView(recyclerView); 391b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 392b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar }); 393b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 394b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 395310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar protected FrameLayout getRecyclerViewContainer() { 396a90fb62f06861beb3af9f9b3356ef0bb0685547cYigit Boyar return getActivity().getContainer(); 397310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 398310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 39942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas protected void requestLayoutOnUIThread(final View view) throws Throwable { 40042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 40142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas @Override 40242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void run() { 40342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas view.requestLayout(); 40442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas } 40542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas }); 406b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 407b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 40842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas protected void scrollBy(final int dt) throws Throwable { 40942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 41042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas @Override 41142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void run() { 41242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 41342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mRecyclerView.scrollBy(dt, 0); 41442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas } else { 41542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mRecyclerView.scrollBy(0, dt); 416e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar } 417d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 41842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas } 41942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas }); 42042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas } 421c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 42242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas protected void smoothScrollBy(final int dt) throws Throwable { 42342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 42442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas @Override 42542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas public void run() { 42642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas if (mRecyclerView.getLayoutManager().canScrollHorizontally()) { 42742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mRecyclerView.smoothScrollBy(dt, 0); 42842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas } else { 42942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mRecyclerView.smoothScrollBy(0, dt); 430c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 43142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas 43242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas } 43342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas }); 434c587f7dba5a337169e854e235da59f595255d6ccAga Madurska getInstrumentation().waitForIdleSync(); 435c587f7dba5a337169e854e235da59f595255d6ccAga Madurska } 436c587f7dba5a337169e854e235da59f595255d6ccAga Madurska 4378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPosition(final int position) throws Throwable { 43842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 4398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 4418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.getLayoutManager().scrollToPosition(position); 4428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 4448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 446959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar void smoothScrollToPosition(final int position) throws Throwable { 447959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar smoothScrollToPosition(position, true); 448959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 449959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar 450959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar void smoothScrollToPosition(final int position, boolean assertArrival) throws Throwable { 451619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 452619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling to " + position); 453619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 454959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar final CountDownLatch viewAdded = new CountDownLatch(1); 455959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar final RecyclerView.OnChildAttachStateChangeListener listener = 456959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar new RecyclerView.OnChildAttachStateChangeListener() { 457959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 458959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void onChildViewAttachedToWindow(View view) { 459959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar if (position == mRecyclerView.getChildAdapterPosition(view)) { 460959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar viewAdded.countDown(); 461959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 462959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 463959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 464959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void onChildViewDetachedFromWindow(View view) { 465959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 466959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar }; 467959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar final AtomicBoolean addedListener = new AtomicBoolean(false); 46842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 469959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 470959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void run() { 471959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar RecyclerView.ViewHolder viewHolderForAdapterPosition = 472959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar mRecyclerView.findViewHolderForAdapterPosition(position); 473959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar if (viewHolderForAdapterPosition != null) { 474959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar viewAdded.countDown(); 475959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } else { 476959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar mRecyclerView.addOnChildAttachStateChangeListener(listener); 477959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar addedListener.set(true); 478959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 479959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar 480959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 481959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar }); 48242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 4838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 4848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 4858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.smoothScrollToPosition(position); 4868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 4878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 488de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar getInstrumentation().waitForIdleSync(); 489959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar assertThat("should be able to scroll in 10 seconds", !assertArrival || 4909c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri viewAdded.await(10, TimeUnit.SECONDS), 491959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar CoreMatchers.is(true)); 492959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar waitForIdleScroll(mRecyclerView); 493619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (mDebug) { 494619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar Log.d(TAG, "SMOOTH scrolling done"); 495619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 496959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar if (addedListener.get()) { 49742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 498959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 499959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void run() { 500959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar mRecyclerView.removeOnChildAttachStateChangeListener(listener); 501959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 502959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar }); 503959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 504504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 5058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 5068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 507ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu void freezeLayout(final boolean freeze) throws Throwable { 50842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 509ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu @Override 510ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu public void run() { 511ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu mRecyclerView.setLayoutFrozen(freeze); 512ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu } 513ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu }); 514ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu } 515ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu 5164bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar public void setVisibility(final View view, final int visibility) throws Throwable { 51742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 5184bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar @Override 5194bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar public void run() { 5204bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar view.setVisibility(visibility); 5214bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar } 5224bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar }); 5234bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar } 5244bcf88a56f3048a7ead6f423faa8fa08b95c5daeYigit Boyar 5253d37afbbf7bf547c1e1c541df9be9696da668884Yigit Boyar public class TestViewHolder extends RecyclerView.ViewHolder { 526d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 527115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item mBoundItem; 528178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar Object mData; 529d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 530d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder(View itemView) { 531d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase super(itemView); 532310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar itemView.setFocusable(true); 533d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 534504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 535504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 536504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public String toString() { 537178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar return super.toString() + " item:" + mBoundItem + ", data:" + mData; 538178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar } 539178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar 540178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar public Object getData() { 541178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar return mData; 542178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar } 543178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar 544178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar public void setData(Object data) { 545178c4e15a0ebb501d2bb4f3ac3176e4c44954954Yigit Boyar mData = data; 546504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 547d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 548da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar class DumbLayoutManager extends TestLayoutManager { 549da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar @Override 550da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 551da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar detachAndScrapAttachedViews(recycler); 552da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutRange(recycler, 0, state.getItemCount()); 553da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar if (layoutLatch != null) { 554da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar layoutLatch.countDown(); 555da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 556da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 557da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar } 5589c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 559e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public class TestLayoutManager extends RecyclerView.LayoutManager { 560f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes int mScrollVerticallyAmount; 561f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes int mScrollHorizontallyAmount; 562e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected CountDownLatch layoutLatch; 563e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar private boolean mSupportsPredictive = false; 564d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 565d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void expectLayouts(int count) { 566d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase layoutLatch = new CountDownLatch(count); 567d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 568d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 56966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void waitForLayout(int seconds) throws Throwable { 57066826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.await(seconds * (mDebug ? 1000 : 1), SECONDS); 57166826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar checkForMainThreadException(); 57266826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar MatcherAssert.assertThat("all layouts should complete on time", 57366826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar layoutLatch.getCount(), CoreMatchers.is(0L)); 57466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar // use a runnable to ensure RV layout is finished 57566826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar getInstrumentation().runOnMainSync(new Runnable() { 57666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar @Override 57766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar public void run() { 57866826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar } 57966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar }); 580629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar } 581629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar 582e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar public boolean isSupportsPredictive() { 583e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar return mSupportsPredictive; 584e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar } 585e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar 586e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar public void setSupportsPredictive(boolean supportsPredictive) { 587e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar mSupportsPredictive = supportsPredictive; 588e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar } 589e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar 590e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar @Override 591e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar public boolean supportsPredictiveItemAnimations() { 592e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar return mSupportsPredictive; 593e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar } 594e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar 595b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertLayoutCount(int count, String msg, long timeout) throws Throwable { 596b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 597b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertEquals(msg, count, layoutLatch.getCount()); 598b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 599b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 600b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void assertNoLayout(String msg, long timeout) throws Throwable { 601b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar layoutLatch.await(timeout, TimeUnit.SECONDS); 602b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertFalse(msg, layoutLatch.getCount() == 0); 603b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 604b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 605d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 606d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public RecyclerView.LayoutParams generateDefaultLayoutParams() { 607d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 608d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase ViewGroup.LayoutParams.WRAP_CONTENT); 609d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 610d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 611d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase void assertVisibleItemPositions() { 612d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int i = getChildCount(); 613d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter(); 614d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase while (i-- > 0) { 615d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = getChildAt(i); 616d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 617115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar Item item = ((TestViewHolder) lp.mViewHolder).mBoundItem; 618b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 619d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "testing item " + i); 620d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 6218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (!lp.isItemRemoved()) { 6228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view); 6238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar assertSame("item position in LP should match adapter value :" + vh, 6248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar testAdapter.mItems.get(vh.mPosition), item); 6258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 626d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 627d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 628d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 629b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar RecyclerView.LayoutParams getLp(View v) { 630b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar return (RecyclerView.LayoutParams) v.getLayoutParams(); 631b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 632b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 633e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar protected void layoutRange(RecyclerView.Recycler recycler, int start, int end) { 634fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar assertScrap(recycler); 635b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 636d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "will layout items from " + start + " to " + end); 637d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 6385ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar int diff = end > start ? 1 : -1; 639504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int top = 0; 6405ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar for (int i = start; i != end; i+=diff) { 641b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar if (mDebug) { 642d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase Log.d(TAG, "laying out item " + i); 643d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 644d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase View view = recycler.getViewForPosition(i); 645b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar assertNotNull("view should not be null for valid position. " 646b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar + "got null view at position " + i, view); 647c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar if (!mRecyclerView.mState.isPreLayout()) { 648c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view 649c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar .getLayoutParams(); 650c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar assertFalse("In post layout, getViewForPosition should never return a view " 651c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar + "that is removed", layoutParams != null 652c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar && layoutParams.isItemRemoved()); 653c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 654b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 6558da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar assertEquals("getViewForPosition should return correct position", 6568da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar i, getPosition(view)); 657c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar addView(view); 658d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase measureChildWithMargins(view, 0, 0); 659e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) { 660e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar layoutDecorated(view, getWidth() - getDecoratedMeasuredWidth(view), top, 661e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getWidth(), top + getDecoratedMeasuredHeight(view)); 662e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 663e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view) 664e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar , top + getDecoratedMeasuredHeight(view)); 665e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 666e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 667504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar top += view.getMeasuredHeight(); 668d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 669d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 670fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar 671fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar private void assertScrap(RecyclerView.Recycler recycler) { 672e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (mRecyclerView.getAdapter() != null && 673e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar !mRecyclerView.getAdapter().hasStableIds()) { 674e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) { 675e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid()); 676e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 677fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 678fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar } 679bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 680bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 681bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollHorizontally() { 682bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 683bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 684bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 685bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 686bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public boolean canScrollVertically() { 687bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return true; 688bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 689bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 690bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 691bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, 692bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 693f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes mScrollHorizontallyAmount += dx; 694bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dx; 695bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 696bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar 697bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar @Override 698bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 699bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar RecyclerView.State state) { 700f129f1b050d2542a91fe8175eac30183beb07b41Chris Banes mScrollVerticallyAmount += dy; 701bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar return dy; 702bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar } 7039290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7049290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar // START MOCKITO OVERRIDES 7059290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar // We override package protected methods to make them public. This is necessary to run 7069290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar // mockito on Kitkat 7079290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7089290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void setRecyclerView(RecyclerView recyclerView) { 7099290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.setRecyclerView(recyclerView); 7109290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7119290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7129290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7139290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void dispatchAttachedToWindow(RecyclerView view) { 7149290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.dispatchAttachedToWindow(view); 7159290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7169290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7179290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7189290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void dispatchDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { 7199290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.dispatchDetachedFromWindow(view, recycler); 7209290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7219290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7229290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7239290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void setExactMeasureSpecsFrom(RecyclerView recyclerView) { 7249290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.setExactMeasureSpecsFrom(recyclerView); 7259290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7269290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7279290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7289290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void setMeasureSpecs(int wSpec, int hSpec) { 7299290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.setMeasureSpecs(wSpec, hSpec); 7309290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7319290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7329290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7339290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) { 7349290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.setMeasuredDimensionFromChildren(widthSpec, heightSpec); 7359290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7369290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7379290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7389290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, 7399290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar RecyclerView.LayoutParams lp) { 7409290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar return super.shouldReMeasureChild(child, widthSpec, heightSpec, lp); 7419290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7429290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7439290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7449290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, 7459290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar RecyclerView.LayoutParams lp) { 7469290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar return super.shouldMeasureChild(child, widthSpec, heightSpec, lp); 7479290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7489290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7499290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7509290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void removeAndRecycleScrapInt(RecyclerView.Recycler recycler) { 7519290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.removeAndRecycleScrapInt(recycler); 7529290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7539290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7549290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar @Override 7559290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar public void stopSmoothScroller() { 7569290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar super.stopSmoothScroller(); 7579290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar } 7589290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar 7599290baf2c13c27d6f6d517b198a3fc079b83f297Yigit Boyar // END MOCKITO OVERRIDES 760d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 761d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 762d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase static class Item { 76311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final static AtomicInteger idCounter = new AtomicInteger(0); 76411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar final public int mId = idCounter.incrementAndGet(); 765d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 7662d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar int mAdapterIndex; 767d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 7689eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu String mText; 7699051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar int mType = 0; 77065dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri boolean mFocusable; 771d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 7722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar Item(int adapterIndex, String text) { 7732d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mAdapterIndex = adapterIndex; 7742d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mText = text; 77565dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri mFocusable = true; 77665dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri } 77765dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri 77865dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri public boolean isFocusable() { 77965dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri return mFocusable; 78065dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri } 78165dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri 78265dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri public void setFocusable(boolean mFocusable) { 78365dea0f8960efe2487c05abeeccb92689bab1332Keyvan Amiri this.mFocusable = mFocusable; 784d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 7858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 7868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 7878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public String toString() { 7888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar return "Item{" + 7898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar "mId=" + mId + 7902d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", originalIndex=" + mAdapterIndex + 7912d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar ", text='" + mText + '\'' + 7928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar '}'; 7938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 794d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 795d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 796e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public class TestAdapter extends RecyclerView.Adapter<TestViewHolder> 79790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar implements AttachDetachCountingAdapter { 798d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 7993d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public static final String DEFAULT_ITEM_PREFIX = "Item "; 8003d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 80190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter mAttachmentCounter = new ViewAttachDetachCounter(); 802d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase List<Item> mItems; 8033911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska final @Nullable RecyclerView.LayoutParams mLayoutParams; 804d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 805e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public TestAdapter(int count) { 8063911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska this(count, null); 8073911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska } 8083911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska 8093911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska public TestAdapter(int count, @Nullable RecyclerView.LayoutParams layoutParams) { 810d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase mItems = new ArrayList<Item>(count); 8113d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev addItems(0, count, DEFAULT_ITEM_PREFIX); 8123911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska mLayoutParams = layoutParams; 8133d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 8143d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 8153d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev private void addItems(int pos, int count, String prefix) { 8163d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev for (int i = 0; i < count; i++, pos++) { 8173d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mItems.add(pos, new Item(pos, prefix)); 818d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 819d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 820d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 821d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 8229051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar public int getItemViewType(int position) { 8239051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar return getItemAt(position).mType; 8249051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar } 8259051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar 8269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar @Override 82790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttachedToWindow(TestViewHolder holder) { 82890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewAttachedToWindow(holder); 82990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewAttached(holder); 83090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 83190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 83390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetachedFromWindow(TestViewHolder holder) { 83490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onViewDetachedFromWindow(holder); 83590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onViewDetached(holder); 83690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 83790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 83890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 83990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttachedToRecyclerView(RecyclerView recyclerView) { 84090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onAttachedToRecyclerView(recyclerView); 84190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onAttached(recyclerView); 84290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 84390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 84490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 84590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetachedFromRecyclerView(RecyclerView recyclerView) { 84690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar super.onDetachedFromRecyclerView(recyclerView); 84790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachmentCounter.onDetached(recyclerView); 84890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 84990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 85090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 851d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public TestViewHolder onCreateViewHolder(ViewGroup parent, 852d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase int viewType) { 853959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar TextView itemView = new TextView(parent.getContext()); 854959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar itemView.setFocusableInTouchMode(true); 855959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar itemView.setFocusable(true); 856959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar return new TestViewHolder(itemView); 857d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 858d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 859d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 860d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public void onBindViewHolder(TestViewHolder holder, int position) { 861619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertNotNull(holder.mOwnerRecyclerView); 862619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(position, holder.getAdapterPosition()); 863d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase final Item item = mItems.get(position); 8644143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mId + ")"); 865115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar holder.mBoundItem = item; 8663911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska if (mLayoutParams != null) { 8673911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(mLayoutParams)); 8683911e1c2d38e301d5ffbdf11f808fdc593dd83e9Aga Madurska } 869d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 870d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 871888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar public Item getItemAt(int position) { 872888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return mItems.get(position); 873888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 874888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 875619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar @Override 876619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar public void onViewRecycled(TestViewHolder holder) { 877619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar super.onViewRecycled(holder); 878619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final int adapterPosition = holder.getAdapterPosition(); 879619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar final boolean shouldHavePosition = !holder.isRemoved() && holder.isBound() && 880619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar !holder.isAdapterPositionUnknown() && !holder.isInvalid(); 881619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar String log = "Position check for " + holder.toString(); 882619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertEquals(log, shouldHavePosition, adapterPosition != RecyclerView.NO_POSITION); 883619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar if (shouldHavePosition) { 884619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar assertTrue(log, mItems.size() > adapterPosition); 8859eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu // TODO: fix b/36042615 getAdapterPosition() is wrong in 8869eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu // consumePendingUpdatesInOnePass where it applies pending change to already 8879eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu // modified position. 8889eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu if (holder.mPreLayoutPosition == RecyclerView.NO_POSITION) { 8899eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu assertSame(log, holder.mBoundItem, mItems.get(adapterPosition)); 8909eeb624e8d3a9e7dc4e70eec759ab60104aab459Dake Gu } 891619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 892619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar } 893619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar 894b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int start, final int count) throws Throwable { 895b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar deleteAndNotify(new int[]{start, count}); 896b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 897b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 898b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar /** 899b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Deletes items in the given ranges. 900b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 901b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * Note that each operation affects the one after so you should offset them properly. 902b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <p> 903b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with 904b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be 905b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar * A D E. Then it will delete 2,1 which means it will delete E. 906b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar */ 907b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void deleteAndNotify(final int[]... startCountTuples) throws Throwable { 9088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : startCountTuples) { 9098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar tuple[1] = -tuple[1]; 9108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 91142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new AddRemoveRunnable(startCountTuples)); 912d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 913d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 914c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar @Override 915c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar public long getItemId(int position) { 916c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar return hasStableIds() ? mItems.get(position).mId : super.getItemId(position); 917c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar } 918c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar 9192d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar public void offsetOriginalIndices(int start, int offset) { 9202d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = start; i < mItems.size(); i++) { 9212d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar mItems.get(i).mAdapterIndex += offset; 9222d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 9232d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar } 9242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar 925e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar /** 926e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param start inclusive 927e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param end exclusive 928e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar * @param offset 929e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar */ 930e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void offsetOriginalIndicesBetween(int start, int end, int offset) { 931e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = start; i < end && i < mItems.size(); i++) { 932e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar mItems.get(i).mAdapterIndex += offset; 933e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 934e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 935e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 9363d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void addAndNotify(final int count) throws Throwable { 9373d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertEquals(0, mItems.size()); 93842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread( 93942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas new AddRemoveRunnable(DEFAULT_ITEM_PREFIX, new int[]{0, count})); 9403d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 9413d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 942959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void resetItemsTo(final List<Item> testItems) throws Throwable { 943959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar if (!mItems.isEmpty()) { 944959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar deleteAndNotify(0, mItems.size()); 945959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 946959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar mItems = testItems; 94742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 948959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar @Override 949959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar public void run() { 950959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar notifyItemRangeInserted(0, testItems.size()); 951959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 952959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar }); 953959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar } 954959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar 955b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int start, final int count) throws Throwable { 956b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar addAndNotify(new int[]{start, count}); 957b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar } 958b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 959b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar public void addAndNotify(final int[]... startCountTuples) throws Throwable { 96042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new AddRemoveRunnable(startCountTuples)); 9618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 962b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar 963cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void dispatchDataSetChanged() throws Throwable { 96442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 96522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar @Override 96622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar public void run() { 96722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar notifyDataSetChanged(); 96822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 96922b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar }); 97022b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar } 97122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar 972cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changeAndNotify(final int start, final int count) throws Throwable { 97342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 9747e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar @Override 9757e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar public void run() { 9767e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar notifyItemRangeChanged(start, count); 9777e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 9787e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar }); 9797e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar } 9807e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar 981121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar public void changeAndNotifyWithPayload(final int start, final int count, 982121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar final Object payload) throws Throwable { 98342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 984121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar @Override 985121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar public void run() { 986121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar notifyItemRangeChanged(start, count, payload); 987121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar } 988121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar }); 989121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar } 990121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar 991cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void changePositionsAndNotify(final int... positions) throws Throwable { 99242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 993cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase @Override 994cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase public void run() { 995cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase for (int i = 0; i < positions.length; i += 1) { 996cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase TestAdapter.super.notifyItemRangeChanged(positions[i], 1); 997cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 998cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 999cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase }); 1000cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase } 1001cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase 10028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar /** 10038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * Similar to other methods but negative count means delete and position count means add. 10048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * <p> 10058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an 10068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar * item to index 1, then remove an item from index 2 (updated index 2) 10078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar */ 10088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable { 100942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new AddRemoveRunnable(startCountTuples)); 1010d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 1011d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase 1012d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase @Override 1013d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase public int getItemCount() { 1014d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase return mItems.size(); 1015d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase } 10168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 1017115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 1018115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 1019115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 1020e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable { 1021e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar for (int i = 0; i < fromToTuples.length; i += 1) { 1022e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar int[] tuple = fromToTuples[i]; 1023e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar moveItem(tuple[0], tuple[1], false); 1024e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 1025e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 1026cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase dispatchDataSetChanged(); 1027e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 1028e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 1029e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 1030115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 1031115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyDataSetChanged 1032115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 1033e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void moveItem(final int from, final int to, final boolean notifyChange) 1034e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar throws Throwable { 103542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1036e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar @Override 1037e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar public void run() { 1038ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar moveInUIThread(from, to); 1039e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar if (notifyChange) { 1040e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar notifyDataSetChanged(); 1041e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 1042e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 1043e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar }); 1044e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar } 1045e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar 1046115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar /** 1047115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar * Uses notifyItemMoved 1048115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar */ 1049115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void moveAndNotify(final int from, final int to) throws Throwable { 105042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas mActivityRule.runOnUiThread(new Runnable() { 1051115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar @Override 1052115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar public void run() { 1053ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar moveInUIThread(from, to); 1054115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar notifyItemMoved(from, to); 1055115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 1056115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar }); 1057115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar } 1058115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 10593d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void clearOnUIThread() { 10603d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertEquals("clearOnUIThread called from a wrong thread", 10613d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev Looper.getMainLooper(), Looper.myLooper()); 10623d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mItems = new ArrayList<Item>(); 10633d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev notifyDataSetChanged(); 10643d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 10653d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 1066ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar protected void moveInUIThread(int from, int to) { 1067ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar Item item = mItems.remove(from); 1068ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar offsetOriginalIndices(from, -1); 1069ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar mItems.add(to, item); 1070ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar offsetOriginalIndices(to + 1, 1); 1071ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar item.mAdapterIndex = to; 1072ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar } 1073115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 1074115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar 107590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar @Override 107690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public ViewAttachDetachCounter getCounter() { 107790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return mAttachmentCounter; 107890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 107990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 10808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private class AddRemoveRunnable implements Runnable { 10813d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev final String mNewItemPrefix; 10828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar final int[][] mStartCountTuples; 10838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10843d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public AddRemoveRunnable(String newItemPrefix, int[]... startCountTuples) { 10853d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mNewItemPrefix = newItemPrefix; 10868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mStartCountTuples = startCountTuples; 10878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 10888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 10893d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public AddRemoveRunnable(int[][] startCountTuples) { 10903d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev this("new item ", startCountTuples); 10913d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 10923d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar @Override 10948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar public void run() { 10958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar for (int[] tuple : mStartCountTuples) { 10968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar if (tuple[1] < 0) { 10978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar delete(tuple); 10988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } else { 10998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar add(tuple); 11008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void add(int[] tuple) { 11052d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar // offset others 11062d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0], tuple[1]); 11073d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev addItems(tuple[0], tuple[1], mNewItemPrefix); 11088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar notifyItemRangeInserted(tuple[0], tuple[1]); 11098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 11118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private void delete(int[] tuple) { 11122d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar final int count = -tuple[1]; 11132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar offsetOriginalIndices(tuple[0] + count, tuple[1]); 11142d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar for (int i = 0; i < count; i++) { 11158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar mItems.remove(tuple[0]); 11168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar notifyItemRangeRemoved(tuple[0], count); 11188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar } 11218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar 112290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public boolean isMainThread() { 112390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar return Looper.myLooper() == Looper.getMainLooper(); 112490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 112590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 1126c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar static class TargetTuple { 1127c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 1128c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mPosition; 1129c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 1130c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int mLayoutDirection; 1131c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 1132c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar TargetTuple(int position, int layoutDirection) { 1133c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mPosition = position; 1134c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar this.mLayoutDirection = layoutDirection; 1135c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 1136c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 1137c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar @Override 1138c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public String toString() { 1139c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return "TargetTuple{" + 1140c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar "mPosition=" + mPosition + 1141c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ", mLayoutDirection=" + mLayoutDirection + 1142c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar '}'; 1143c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 1144c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 114590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 114690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public interface AttachDetachCountingAdapter { 114790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 114890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar ViewAttachDetachCounter getCounter(); 114990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 115090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 115190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public class ViewAttachDetachCounter { 115290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 115390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar Set<RecyclerView.ViewHolder> mAttachedSet = new HashSet<RecyclerView.ViewHolder>(); 115490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 115590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void validateRemaining(RecyclerView recyclerView) { 115690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 115790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i++) { 115890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 115990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view); 116090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("remaining view should be in attached set " + vh, 116190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.contains(vh)); 116290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 116390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertEquals("there should not be any views left in attached set", 116490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar childCount, mAttachedSet.size()); 116590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 116690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 116790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewDetached(RecyclerView.ViewHolder viewHolder) { 116890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 116990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should be in attached set", 117090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.remove(viewHolder)); 117190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 117290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 117390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 117490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 117590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 117690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onViewAttached(RecyclerView.ViewHolder viewHolder) { 117790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar try { 117890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar assertTrue("view holder should not be in attached set", 117990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(viewHolder)); 118090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } catch (Throwable t) { 118190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar postExceptionToInstrumentation(t); 118290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 118390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 118490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 118590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onAttached(RecyclerView recyclerView) { 118690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar // when a new RV is attached, clear the set and add all view holders 118790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.clear(); 118890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar final int childCount = recyclerView.getChildCount(); 118990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar for (int i = 0; i < childCount; i ++) { 119090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar View view = recyclerView.getChildAt(i); 119190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar mAttachedSet.add(recyclerView.getChildViewHolder(view)); 119290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 119390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 119490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar 119590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar public void onDetached(RecyclerView recyclerView) { 119690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar validateRemaining(recyclerView); 119790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 119890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar } 11999c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 12009c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri /** 12019c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * Returns whether a child of RecyclerView is partially in bound. A child is 12029c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * partially in-bounds if it's either fully or partially visible on the screen. 12039c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param parent The RecyclerView holding the child. 12049c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param child The child view to be checked whether is partially (or fully) within RV's bounds. 12059c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @return True if the child view is partially (or fully) visible; false otherwise. 12069c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri */ 12079c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri public static boolean isViewPartiallyInBound(RecyclerView parent, View child) { 12089c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (child == null) { 12099c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return false; 12109c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 12119c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentLeft = parent.getPaddingLeft(); 12129c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentTop = parent.getPaddingTop(); 12139c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentRight = parent.getWidth() - parent.getPaddingRight(); 12149c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentBottom = parent.getHeight() - parent.getPaddingBottom(); 12159c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 12169c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childLeft = child.getLeft() - child.getScrollX(); 12179c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childTop = child.getTop() - child.getScrollY(); 12189c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childRight = child.getRight() - child.getScrollX(); 12199c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childBottom = child.getBottom() - child.getScrollY(); 12209c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 12219c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (childLeft >= parentRight || childRight <= parentLeft 12229c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri || childTop >= parentBottom || childBottom <= parentTop) { 12239c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return false; 12249c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 12259c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return true; 12269c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 12279c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 12289c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri /** 12299c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * Returns whether a child of RecyclerView is fully in-bounds, that is it's fully visible 12309c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * on the screen. 12319c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param parent The RecyclerView holding the child. 12329c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @param child The child view to be checked whether is fully within RV's bounds. 12339c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri * @return True if the child view is fully visible; false otherwise. 12349c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri */ 12359c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri public boolean isViewFullyInBound(RecyclerView parent, View child) { 12369c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (child == null) { 12379c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return false; 12389c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 12399c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentLeft = parent.getPaddingLeft(); 12409c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentTop = parent.getPaddingTop(); 12419c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentRight = parent.getWidth() - parent.getPaddingRight(); 12429c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int parentBottom = parent.getHeight() - parent.getPaddingBottom(); 12439c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 12449c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childLeft = child.getLeft() - child.getScrollX(); 12459c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childTop = child.getTop() - child.getScrollY(); 12469c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childRight = child.getRight() - child.getScrollX(); 12479c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri final int childBottom = child.getBottom() - child.getScrollY(); 12489c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri 12499c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri if (childLeft >= parentLeft && childRight <= parentRight 12509c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri && childTop >= parentTop && childBottom <= parentBottom) { 12519c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return true; 12529c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 12539c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri return false; 12549c0ad7d5adfbe51d85adcbc056b6183095d8aaedKeyvan Amiri } 12557a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar} 1256