BaseRecyclerViewInstrumentationTest.java revision e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950
1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/*
2d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Copyright (C) 2014 The Android Open Source Project
3d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
4d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Licensed under the Apache License, Version 2.0 (the "License");
5d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * you may not use this file except in compliance with the License.
6d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * You may obtain a copy of the License at
7d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
8d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *      http://www.apache.org/licenses/LICENSE-2.0
9d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
10d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Unless required by applicable law or agreed to in writing, software
11d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * distributed under the License is distributed on an "AS IS" BASIS,
12d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * See the License for the specific language governing permissions and
14d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * limitations under the License.
15d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase */
16d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
17d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepackage android.support.v7.widget;
18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
192bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport android.app.Instrumentation;
20c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport android.graphics.Rect;
218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport android.os.Looper;
22e2437126f0225135891544315ff72e54c7de0f59Yigit Boyarimport android.support.v4.view.ViewCompat;
23d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2;
24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log;
25e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.LayoutInflater;
26d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View;
27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup;
28310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout;
29d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView;
30d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
312bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.InvocationTargetException;
322bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyarimport java.lang.reflect.Method;
33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList;
3490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.HashSet;
35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List;
3690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyarimport java.util.Set;
37d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch;
38d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit;
3911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
40da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyarimport java.util.concurrent.locks.Lock;
41da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyarimport java.util.concurrent.locks.ReentrantLock;
42e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.recyclerview.test.R;
43d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
44d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends
45d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        ActivityInstrumentationTestCase2<TestActivity> {
46d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
47d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    private static final String TAG = "RecyclerViewTest";
48d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private boolean mDebug;
50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    protected RecyclerView mRecyclerView;
52d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    protected AdapterHelper mAdapterHelper;
548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
557e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar    Throwable mainThreadException;
567e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
57d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    public BaseRecyclerViewInstrumentationTest() {
58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        this(false);
59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
61b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public BaseRecyclerViewInstrumentationTest(boolean debug) {
628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        super("android.support.v7.recyclerview", TestActivity.class);
63b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mDebug = debug;
64b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
65b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar    void checkForMainThreadException() throws Throwable {
677e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        if (mainThreadException != null) {
687e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            throw mainThreadException;
697e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        }
707e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar    }
717e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
72e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    void setHasTransientState(final View view, final boolean value) {
73e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        try {
74e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            runTestOnUiThread(new Runnable() {
75e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                @Override
76e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                public void run() {
77e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    ViewCompat.setHasTransientState(view, value);
78e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
79e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            });
80e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        } catch (Throwable throwable) {
81e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            Log.e(TAG, "", throwable);
82e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        }
83e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    }
84e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar
852bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar    protected void enableAccessibility()
862bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
872bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar        Method getUIAutomation = Instrumentation.class.getMethod("getUiAutomation");
882bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar        getUIAutomation.invoke(getInstrumentation());
892bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar    }
902bf89a9bae2b6110a77cf0b3754b567bb07d12a5Yigit Boyar
910b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    void setAdapter(final RecyclerView.Adapter adapter) throws Throwable {
920b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        runTestOnUiThread(new Runnable() {
930b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
940b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            public void run() {
950b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                mRecyclerView.setAdapter(adapter);
960b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
970b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        });
980b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
990b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
100e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    protected WrappedRecyclerView inflateWrappedRV() {
101e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        return (WrappedRecyclerView)
102e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                LayoutInflater.from(getActivity()).inflate(R.layout.wrapped_test_rv,
103e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                        getRecyclerViewContainer(), false);
104e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    }
105e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar
1060b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    void swapAdapter(final RecyclerView.Adapter adapter,
1070b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            final boolean removeAndRecycleExistingViews) throws Throwable {
1080b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        runTestOnUiThread(new Runnable() {
1090b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            @Override
1100b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            public void run() {
111824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                try {
112824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews);
113824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                } catch (Throwable t) {
114824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                    postExceptionToInstrumentation(t);
115824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar                }
1160b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            }
1170b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        });
118824a7db96feacf3b68f56993ed72712bc935153eYigit Boyar        checkForMainThreadException();
1190b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
1200b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar
1217e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar    void postExceptionToInstrumentation(Throwable t) {
1226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        if (mainThreadException != null) {
1236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            Log.e(TAG, "receiving another main thread exception. dropping.", t);
1246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        } else {
12590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            Log.e(TAG, "captured exception on main thread", t);
1266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            mainThreadException = t;
1276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        }
1286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
1297e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        if (mRecyclerView != null && mRecyclerView
1307e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                .getLayoutManager() instanceof TestLayoutManager) {
1317e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager();
1327e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            // finish all layouts so that we get the correct exception
1337e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            while (lm.layoutLatch.getCount() > 0) {
1347e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                lm.layoutLatch.countDown();
1357e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            }
1367e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        }
1377e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar    }
1387e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
1398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    @Override
1408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    protected void tearDown() throws Exception {
1418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (mRecyclerView != null) {
1428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            try {
1438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                removeRecyclerView();
1448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } catch (Throwable throwable) {
1458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                throwable.printStackTrace();
1468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
1478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getInstrumentation().waitForIdleSync();
149ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar        super.tearDown();
150ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar
1516e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        try {
1526e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            checkForMainThreadException();
1536e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        } catch (Exception e) {
1546e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar            throw e;
1556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        } catch (Throwable throwable) {
156ca9ce7ccdccf6b6a2a81da73dd1e6425d90e4b23Yigit Boyar            throw new Exception(throwable);
1576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        }
1588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
1598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
160c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    public Rect getDecoratedRecyclerViewBounds() {
161c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        return new Rect(
162c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                mRecyclerView.getPaddingLeft(),
163c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                mRecyclerView.getPaddingTop(),
164c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                mRecyclerView.getPaddingLeft() + mRecyclerView.getWidth(),
165c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                mRecyclerView.getPaddingTop() + mRecyclerView.getHeight()
166c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        );
167c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
168c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
169b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void removeRecyclerView() throws Throwable {
170d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        if (mRecyclerView == null) {
171d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar            return;
172d7e2f2ab1d253133fa54f309e74a7ee384c33971Yigit Boyar        }
17390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        if (!isMainThread()) {
17490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            getInstrumentation().waitForIdleSync();
17590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
176b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        runTestOnUiThread(new Runnable() {
177b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
178b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            public void run() {
179abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                try {
180abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    final RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
181abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    if (adapter instanceof AttachDetachCountingAdapter) {
182abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                        ((AttachDetachCountingAdapter) adapter).getCounter()
183abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                                .validateRemaining(mRecyclerView);
184abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    }
185abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    getActivity().mContainer.removeAllViews();
186abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                } catch (Throwable t) {
187abd0fbfcdd5b438909f126392eca5978b5939b6dYigit Boyar                    postExceptionToInstrumentation(t);
18890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                }
189b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        });
19190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        mRecyclerView = null;
192b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
193b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
194504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    void waitForAnimations(int seconds) throws InterruptedException {
195504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final CountDownLatch latch = new CountDownLatch(2);
196504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        boolean running = mRecyclerView.mItemAnimator
197504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                .isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
198504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    @Override
199504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    public void onAnimationsFinished() {
200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                        latch.countDown();
201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    }
202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                });
203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        if (running) {
204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            latch.await(seconds, TimeUnit.SECONDS);
205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
208542f1260934df280985294eaef1ec8469863281fYigit Boyar    public boolean requestFocus(final View view) {
209542f1260934df280985294eaef1ec8469863281fYigit Boyar        final boolean[] result = new boolean[1];
210542f1260934df280985294eaef1ec8469863281fYigit Boyar        getActivity().runOnUiThread(new Runnable() {
211542f1260934df280985294eaef1ec8469863281fYigit Boyar            @Override
212542f1260934df280985294eaef1ec8469863281fYigit Boyar            public void run() {
213542f1260934df280985294eaef1ec8469863281fYigit Boyar                result[0] = view.requestFocus();
214542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
215542f1260934df280985294eaef1ec8469863281fYigit Boyar        });
216542f1260934df280985294eaef1ec8469863281fYigit Boyar        return result[0];
217542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
218542f1260934df280985294eaef1ec8469863281fYigit Boyar
219b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void setRecyclerView(final RecyclerView recyclerView) throws Throwable {
2200b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        setRecyclerView(recyclerView, true);
2210b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    }
2220b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar    public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool)
2230b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            throws Throwable {
224e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        setRecyclerView(recyclerView, true, true);
225e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar    }
226e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar    public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool,
227e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            boolean addPositionCheckItemAnimator)
228e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            throws Throwable {
229b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mRecyclerView = recyclerView;
2300b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        if (assignDummyPool) {
2310b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() {
2320b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                @Override
2330b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                public RecyclerView.ViewHolder getRecycledView(int viewType) {
2340b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType);
2350b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    if (viewHolder == null) {
2360b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                        return null;
2370b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    }
2380b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    viewHolder.addFlags(RecyclerView.ViewHolder.FLAG_BOUND);
2390b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    viewHolder.mPosition = 200;
2400b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    viewHolder.mOldPosition = 300;
2410b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    viewHolder.mPreLayoutPosition = 500;
2420b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    return viewHolder;
2438da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar                }
2448da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar
2450b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                @Override
2460b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                public void putRecycledView(RecyclerView.ViewHolder scrap) {
247619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    assertNull(scrap.mOwnerRecyclerView);
2480b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                    super.putRecycledView(scrap);
2490b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar                }
2500b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            };
2510b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar            mRecyclerView.setRecycledViewPool(pool);
2520b1059b711009601ff08354e2df6bfc45266e80aYigit Boyar        }
253e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        if (addPositionCheckItemAnimator) {
254e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
255e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                @Override
256e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
257e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                        RecyclerView.State state) {
258e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                    RecyclerView.ViewHolder vh = parent.getChildViewHolder(view);
259e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                    if (!vh.isRemoved()) {
260e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                        assertNotSame("If getItemOffsets is called, child should have a valid"
261619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                                            + " adapter position unless it is removed : " + vh,
262e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                                    vh.getAdapterPosition(), RecyclerView.NO_POSITION);
263e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                    }
264e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar                }
265e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar            });
266e37d2e63f16bc5c2938e372238ba54679a9e578eYigit Boyar        }
2678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mAdapterHelper = recyclerView.mAdapterHelper;
268b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        runTestOnUiThread(new Runnable() {
269b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
270b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            public void run() {
271b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                getActivity().mContainer.addView(recyclerView);
272b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
273b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        });
274b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
275b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
276310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    protected FrameLayout getRecyclerViewContainer() {
277310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        return getActivity().mContainer;
278310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
279310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
280e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    public void requestLayoutOnUIThread(final View view) {
281e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        try {
282e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            runTestOnUiThread(new Runnable() {
283e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                @Override
284e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                public void run() {
285e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    view.requestLayout();
286e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
287e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            });
288e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        } catch (Throwable throwable) {
289e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            Log.e(TAG, "", throwable);
290e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        }
291b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
292b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
293e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar    public void scrollBy(final int dt) {
294e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        try {
295e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            runTestOnUiThread(new Runnable() {
296e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                @Override
297e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                public void run() {
298e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    if (mRecyclerView.getLayoutManager().canScrollHorizontally()) {
299e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                        mRecyclerView.scrollBy(dt, 0);
300e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    } else {
301e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                        mRecyclerView.scrollBy(0, dt);
302e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                    }
303b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
304e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar                }
305e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            });
306e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        } catch (Throwable throwable) {
307e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar            Log.e(TAG, "", throwable);
308e2437126f0225135891544315ff72e54c7de0f59Yigit Boyar        }
309d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
310d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
3118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void scrollToPosition(final int position) throws Throwable {
3128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        runTestOnUiThread(new Runnable() {
3138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            @Override
3148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            public void run() {
3158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                mRecyclerView.getLayoutManager().scrollToPosition(position);
3168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
3178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        });
3188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
3198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
3208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    void smoothScrollToPosition(final int position)
3218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            throws Throwable {
322619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        if (mDebug) {
323619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            Log.d(TAG, "SMOOTH scrolling to " + position);
324619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        }
3258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        runTestOnUiThread(new Runnable() {
3268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            @Override
3278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            public void run() {
3288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                mRecyclerView.smoothScrollToPosition(position);
3298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
3308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        });
331de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        getInstrumentation().waitForIdleSync();
332de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        Thread.sleep(200); //give scroller some time so start
3338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        while (mRecyclerView.getLayoutManager().isSmoothScrolling() ||
3348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
3358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            if (mDebug) {
3368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar                Log.d(TAG, "SMOOTH scrolling step");
3378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            }
3388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar            Thread.sleep(200);
3398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar        }
340619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        if (mDebug) {
341619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            Log.d(TAG, "SMOOTH scrolling done");
342619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        }
343504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
3448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar    }
3458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
346d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    class TestViewHolder extends RecyclerView.ViewHolder {
347d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
348115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        Item mBoundItem;
349d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
350d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public TestViewHolder(View itemView) {
351d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            super(itemView);
352310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar            itemView.setFocusable(true);
353d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
354504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
355504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        @Override
356504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        public String toString() {
357115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            return super.toString() + " item:" + mBoundItem;
358504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
359d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
360da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    class DumbLayoutManager extends TestLayoutManager {
361da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        ReentrantLock mLayoutLock = new ReentrantLock();
362da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        public void blockLayout() {
363da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            mLayoutLock.lock();
364da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        }
365d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
366da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        public void unblockLayout() {
367da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            mLayoutLock.unlock();
368da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        }
369da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        @Override
370da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
371da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            mLayoutLock.lock();
372da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            detachAndScrapAttachedViews(recycler);
373da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            layoutRange(recycler, 0, state.getItemCount());
374da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            if (layoutLatch != null) {
375da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar                layoutLatch.countDown();
376da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            }
377da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar            mLayoutLock.unlock();
378da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar        }
379da50fde358bd3ef2edb273b3bf0265cf5e84ec6bYigit Boyar    }
380e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    public class TestLayoutManager extends RecyclerView.LayoutManager {
381e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        protected CountDownLatch layoutLatch;
382d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
383d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public void expectLayouts(int count) {
384d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            layoutLatch = new CountDownLatch(count);
385d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
386d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
387b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable {
388b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit);
389d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            assertEquals("all expected layouts should be executed at the expected time",
390d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    0, layoutLatch.getCount());
391e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            getInstrumentation().waitForIdleSync();
392d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
393d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
394b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void assertLayoutCount(int count, String msg, long timeout) throws Throwable {
395b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            layoutLatch.await(timeout, TimeUnit.SECONDS);
396b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            assertEquals(msg, count, layoutLatch.getCount());
397b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
398b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
399b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void assertNoLayout(String msg, long timeout) throws Throwable {
400b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            layoutLatch.await(timeout, TimeUnit.SECONDS);
401b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            assertFalse(msg, layoutLatch.getCount() == 0);
402b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
403b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
404b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void waitForLayout(long timeout) throws Throwable {
4058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            waitForLayout(timeout * (mDebug ? 10000 : 1), TimeUnit.SECONDS);
406b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
407b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
408d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
409d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
410d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
411d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    ViewGroup.LayoutParams.WRAP_CONTENT);
412d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
413d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
414d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        void assertVisibleItemPositions() {
415d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            int i = getChildCount();
416d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter();
417d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            while (i-- > 0) {
418d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                View view = getChildAt(i);
419d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
420115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                Item item = ((TestViewHolder) lp.mViewHolder).mBoundItem;
421b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (mDebug) {
422d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    Log.d(TAG, "testing item " + i);
423d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                }
4248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (!lp.isItemRemoved()) {
4258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(view);
4268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    assertSame("item position in LP should match adapter value :" + vh,
4278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                            testAdapter.mItems.get(vh.mPosition), item);
4288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
429d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
430d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
431d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
432b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        RecyclerView.LayoutParams getLp(View v) {
433b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            return (RecyclerView.LayoutParams) v.getLayoutParams();
434b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
435b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
436e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        protected void layoutRange(RecyclerView.Recycler recycler, int start, int end) {
437fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar            assertScrap(recycler);
438b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            if (mDebug) {
439d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                Log.d(TAG, "will layout items from " + start + " to " + end);
440d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
4415ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            int diff = end > start ? 1 : -1;
442504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            int top = 0;
4435ced882cabdcefbb469e332f6f73983999aad0e5Yigit Boyar            for (int i = start; i != end; i+=diff) {
444b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (mDebug) {
445d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    Log.d(TAG, "laying out item " + i);
446d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                }
447d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                View view = recycler.getViewForPosition(i);
448b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                assertNotNull("view should not be null for valid position. "
449b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        + "got null view at position " + i, view);
450c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                if (!mRecyclerView.mState.isPreLayout()) {
451c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view
452c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            .getLayoutParams();
453c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    assertFalse("In post layout, getViewForPosition should never return a view "
454c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            + "that is removed", layoutParams != null
455c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                            && layoutParams.isItemRemoved());
456c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
457b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
4588da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar                assertEquals("getViewForPosition should return correct position",
4598da5bba529a2fe9bd2aafab28010e11ec4de93c7Yigit Boyar                        i, getPosition(view));
460c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                addView(view);
461b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
462d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                measureChildWithMargins(view, 0, 0);
463e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
464e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    layoutDecorated(view, getWidth() - getDecoratedMeasuredWidth(view), top,
465e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                            getWidth(), top + getDecoratedMeasuredHeight(view));
466e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                } else {
467e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                    layoutDecorated(view, 0, top, getDecoratedMeasuredWidth(view)
468e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                            , top + getDecoratedMeasuredHeight(view));
469e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar                }
470e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar
471504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                top += view.getMeasuredHeight();
472d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
473d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
474fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar
475fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar        private void assertScrap(RecyclerView.Recycler recycler) {
476e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            if (mRecyclerView.getAdapter() != null &&
477e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    !mRecyclerView.getAdapter().hasStableIds()) {
478e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                for (RecyclerView.ViewHolder viewHolder : recycler.getScrapList()) {
479e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    assertFalse("Invalid scrap should be no kept", viewHolder.isInvalid());
480e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                }
481fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar            }
482fe3996cf5af65fe11cc3ed47b67123ec267bd336Yigit Boyar        }
483bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
484bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
485bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public boolean canScrollHorizontally() {
486bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return true;
487bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
488bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
489bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
490bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public boolean canScrollVertically() {
491bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return true;
492bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
493bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
494bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
495bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
496bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                RecyclerView.State state) {
497bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return dx;
498bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
499bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar
500bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        @Override
501bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
502bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar                RecyclerView.State state) {
503bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar            return dy;
504bb6ebab3b3b85ec20b3bc262289a2460705322f5Yigit Boyar        }
505d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
506d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
507d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    static class Item {
50811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final static AtomicInteger idCounter = new AtomicInteger(0);
50911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final public int mId = idCounter.incrementAndGet();
510d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
5112d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        int mAdapterIndex;
512d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
5132d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        final String mText;
514d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
5152d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        Item(int adapterIndex, String text) {
5162d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar            mAdapterIndex = adapterIndex;
5172d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar            mText = text;
518d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
5198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public String toString() {
5228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return "Item{" +
5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    "mId=" + mId +
5242d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                    ", originalIndex=" + mAdapterIndex +
5252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                    ", text='" + mText + '\'' +
5268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    '}';
5278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
528d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
529d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
530e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar    public class TestAdapter extends RecyclerView.Adapter<TestViewHolder>
53190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            implements AttachDetachCountingAdapter {
532d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
53390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        ViewAttachDetachCounter mAttachmentCounter = new ViewAttachDetachCounter();
534d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        List<Item> mItems;
535d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
536e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar        public TestAdapter(int count) {
537d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            mItems = new ArrayList<Item>(count);
538d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            for (int i = 0; i < count; i++) {
539d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                mItems.add(new Item(i, "Item " + i));
540d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
541d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
542d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
543d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
54490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onViewAttachedToWindow(TestViewHolder holder) {
54590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            super.onViewAttachedToWindow(holder);
54690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            mAttachmentCounter.onViewAttached(holder);
54790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
54890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
54990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        @Override
55090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onViewDetachedFromWindow(TestViewHolder holder) {
55190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            super.onViewDetachedFromWindow(holder);
55290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            mAttachmentCounter.onViewDetached(holder);
55390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
55490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
55590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        @Override
55690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
55790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            super.onAttachedToRecyclerView(recyclerView);
55890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            mAttachmentCounter.onAttached(recyclerView);
55990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
56090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
56190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        @Override
56290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
56390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            super.onDetachedFromRecyclerView(recyclerView);
56490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            mAttachmentCounter.onDetached(recyclerView);
56590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
56690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
56790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        @Override
568d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public TestViewHolder onCreateViewHolder(ViewGroup parent,
569d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                int viewType) {
570d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            return new TestViewHolder(new TextView(parent.getContext()));
571d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
572d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
573d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
574d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public void onBindViewHolder(TestViewHolder holder, int position) {
575619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            assertNotNull(holder.mOwnerRecyclerView);
576619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            assertEquals(position, holder.getAdapterPosition());
577d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            final Item item = mItems.get(position);
5782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar            ((TextView) (holder.itemView)).setText(item.mText + "(" + item.mAdapterIndex + ")");
579115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            holder.mBoundItem = item;
580d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
581d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
582888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        public Item getItemAt(int position) {
583888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar            return mItems.get(position);
584888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar        }
585888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar
586619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        @Override
587619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        public void onViewRecycled(TestViewHolder holder) {
588619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            super.onViewRecycled(holder);
589619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            final int adapterPosition = holder.getAdapterPosition();
590619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            final boolean shouldHavePosition = !holder.isRemoved() && holder.isBound() &&
591619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                    !holder.isAdapterPositionUnknown() && !holder.isInvalid();
592619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            String log = "Position check for " + holder.toString();
593619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            assertEquals(log, shouldHavePosition, adapterPosition != RecyclerView.NO_POSITION);
594619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            if (shouldHavePosition) {
595619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                assertTrue(log, mItems.size() > adapterPosition);
596619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar                assertSame(log, holder.mBoundItem, mItems.get(adapterPosition));
597619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar            }
598619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar        }
599619ac016831a5ad75c2e0921c9036b6f030a9e63Yigit Boyar
600b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void deleteAndNotify(final int start, final int count) throws Throwable {
601b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            deleteAndNotify(new int[]{start, count});
602b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
603b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
604b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        /**
605b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * Deletes items in the given ranges.
606b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * <p>
607b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * Note that each operation affects the one after so you should offset them properly.
608b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * <p>
609b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with
610b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be
611b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * A D E. Then it will delete 2,1 which means it will delete E.
612b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         */
613b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void deleteAndNotify(final int[]... startCountTuples) throws Throwable {
6148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (int[] tuple : startCountTuples) {
6158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                tuple[1] = -tuple[1];
6168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            new AddRemoveRunnable(startCountTuples).runOnMainThread();
618d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
619d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
620c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        @Override
621c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        public long getItemId(int position) {
622c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            return hasStableIds() ? mItems.get(position).mId : super.getItemId(position);
623c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar        }
624c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar
6252d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        public void offsetOriginalIndices(int start, int offset) {
6262d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar            for (int i = start; i < mItems.size(); i++) {
6272d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                mItems.get(i).mAdapterIndex += offset;
6282d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar            }
6292d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        }
6302d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar
631e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        /**
632e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar         * @param start inclusive
633e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar         * @param end exclusive
634e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar         * @param offset
635e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar         */
636e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        public void offsetOriginalIndicesBetween(int start, int end, int offset) {
637e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            for (int i = start; i < end && i < mItems.size(); i++) {
638e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                mItems.get(i).mAdapterIndex += offset;
639e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
640e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        }
641e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
642b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void addAndNotify(final int start, final int count) throws Throwable {
643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            addAndNotify(new int[]{start, count});
644b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
645b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
646b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void addAndNotify(final int[]... startCountTuples) throws Throwable {
6478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            new AddRemoveRunnable(startCountTuples).runOnMainThread();
6488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
649b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
650cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        public void dispatchDataSetChanged() throws Throwable {
65122b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            runTestOnUiThread(new Runnable() {
65222b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                @Override
65322b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                public void run() {
65422b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                    notifyDataSetChanged();
65522b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar                }
65622b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar            });
65722b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar        }
65822b6b8163d2e15eb6d0a89906854dec801945f1dYigit Boyar
659cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        public void changeAndNotify(final int start, final int count) throws Throwable {
6607e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            runTestOnUiThread(new Runnable() {
6617e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                @Override
6627e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                public void run() {
6637e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                    notifyItemRangeChanged(start, count);
6647e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar                }
6657e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar            });
6667e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar        }
6677e9da1a2629b657d56e2f54abd18c6249f3be037Yigit Boyar
668cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        public void changePositionsAndNotify(final int... positions) throws Throwable {
669cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            runTestOnUiThread(new Runnable() {
670cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                @Override
671cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                public void run() {
672cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    for (int i = 0; i < positions.length; i += 1) {
673cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                        TestAdapter.super.notifyItemRangeChanged(positions[i], 1);
674cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    }
675cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                }
676cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase            });
677cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase        }
678cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase
6798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        /**
6808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar         * Similar to other methods but negative count means delete and position count means add.
6818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar         * <p>
6828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar         * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an
6838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar         * item to index 1, then remove an item from index 2 (updated index 2)
6848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar         */
6858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable {
6868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            new AddRemoveRunnable(startCountTuples).runOnMainThread();
687d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
688d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
689d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
690d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public int getItemCount() {
691d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            return mItems.size();
692d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
6938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
694115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        /**
695115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar         * Uses notifyDataSetChanged
696115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar         */
697e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable {
698e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            for (int i = 0; i < fromToTuples.length; i += 1) {
699e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                int[] tuple = fromToTuples[i];
700e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                moveItem(tuple[0], tuple[1], false);
701e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
702e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            if (notifyChange) {
703cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                dispatchDataSetChanged();
704e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
705e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        }
706e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
707115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        /**
708115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar         * Uses notifyDataSetChanged
709115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar         */
710e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        public void moveItem(final int from, final int to, final boolean notifyChange)
711e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                throws Throwable {
712e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            runTestOnUiThread(new Runnable() {
713e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                @Override
714e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                public void run() {
715ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar                    moveInUIThread(from, to);
716e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    if (notifyChange) {
717e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        notifyDataSetChanged();
718e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    }
719e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                }
720e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            });
721e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        }
722e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
723115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        /**
724115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar         * Uses notifyItemMoved
725115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar         */
726115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        public void moveAndNotify(final int from, final int to) throws Throwable {
727115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            runTestOnUiThread(new Runnable() {
728115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                @Override
729115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                public void run() {
730ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar                    moveInUIThread(from, to);
731115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    notifyItemMoved(from, to);
732115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                }
733115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar            });
734115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        }
735115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
736ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar        protected void moveInUIThread(int from, int to) {
737ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar            Item item = mItems.remove(from);
738ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar            offsetOriginalIndices(from, -1);
739ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar            mItems.add(to, item);
740ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar            offsetOriginalIndices(to + 1, 1);
741ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar            item.mAdapterIndex = to;
742ec60a348ce39889a99f7ca02da94a6867bfdf66fYigit Boyar        }
743115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
744115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar
74590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        @Override
74690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public ViewAttachDetachCounter getCounter() {
74790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            return mAttachmentCounter;
74890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
74990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
7508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private class AddRemoveRunnable implements Runnable {
7528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            final int[][] mStartCountTuples;
7538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public AddRemoveRunnable(int[][] startCountTuples) {
7558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mStartCountTuples = startCountTuples;
7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void runOnMainThread() throws Throwable {
7598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (Looper.myLooper() == Looper.getMainLooper()) {
7608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    run();
7618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                } else {
7628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    runTestOnUiThread(this);
7638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
7648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
7678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void run() {
7688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int[] tuple : mStartCountTuples) {
7698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (tuple[1] < 0) {
7708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        delete(tuple);
7718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    } else {
7728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        add(tuple);
7738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
7748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
7758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            private void add(int[] tuple) {
7782d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                // offset others
7792d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                offsetOriginalIndices(tuple[0], tuple[1]);
7808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int i = 0; i < tuple[1]; i++) {
7818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    mItems.add(tuple[0], new Item(i, "new item " + i));
7828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
7838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                notifyItemRangeInserted(tuple[0], tuple[1]);
7848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            private void delete(int[] tuple) {
7872d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                final int count = -tuple[1];
7882d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                offsetOriginalIndices(tuple[0] + count, tuple[1]);
7892d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                for (int i = 0; i < count; i++) {
7908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    mItems.remove(tuple[0]);
7918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
7922d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar                notifyItemRangeRemoved(tuple[0], count);
7938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
79790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar    public boolean isMainThread() {
79890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        return Looper.myLooper() == Looper.getMainLooper();
79990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar    }
80090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
8018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    @Override
8028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void runTestOnUiThread(Runnable r) throws Throwable {
8038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        if (Looper.myLooper() == Looper.getMainLooper()) {
8048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            r.run();
8058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        } else {
8068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            super.runTestOnUiThread(r);
8078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
808d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
809c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
810c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    static class TargetTuple {
811c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
812c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int mPosition;
813c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
814c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        final int mLayoutDirection;
815c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
816c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        TargetTuple(int position, int layoutDirection) {
817c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            this.mPosition = position;
818c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            this.mLayoutDirection = layoutDirection;
819c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
820c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar
821c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        @Override
822c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        public String toString() {
823c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar            return "TargetTuple{" +
824c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    "mPosition=" + mPosition +
825c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    ", mLayoutDirection=" + mLayoutDirection +
826c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar                    '}';
827c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar        }
828c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar    }
82990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
83090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar    public interface AttachDetachCountingAdapter {
83190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
83290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        ViewAttachDetachCounter getCounter();
83390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar    }
83490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
83590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar    public class ViewAttachDetachCounter {
83690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
83790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        Set<RecyclerView.ViewHolder> mAttachedSet = new HashSet<RecyclerView.ViewHolder>();
83890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
83990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void validateRemaining(RecyclerView recyclerView) {
84090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            final int childCount = recyclerView.getChildCount();
84190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            for (int i = 0; i < childCount; i++) {
84290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                View view = recyclerView.getChildAt(i);
84390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view);
84490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertTrue("remaining view should be in attached set " + vh,
84590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                        mAttachedSet.contains(vh));
84690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            }
84790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            assertEquals("there should not be any views left in attached set",
84890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    childCount, mAttachedSet.size());
84990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
85090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
85190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onViewDetached(RecyclerView.ViewHolder viewHolder) {
85290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            try {
85390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertTrue("view holder should be in attached set",
85490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                        mAttachedSet.remove(viewHolder));
85590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            } catch (Throwable t) {
85690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                postExceptionToInstrumentation(t);
85790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            }
85890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
85990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
86090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onViewAttached(RecyclerView.ViewHolder viewHolder) {
86190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            try {
86290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertTrue("view holder should not be in attached set",
86390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                        mAttachedSet.add(viewHolder));
86490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            } catch (Throwable t) {
86590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                postExceptionToInstrumentation(t);
86690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            }
86790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
86890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
86990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onAttached(RecyclerView recyclerView) {
87090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            // when a new RV is attached, clear the set and add all view holders
87190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            mAttachedSet.clear();
87290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            final int childCount = recyclerView.getChildCount();
87390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            for (int i = 0; i < childCount; i ++) {
87490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                View view = recyclerView.getChildAt(i);
87590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                mAttachedSet.add(recyclerView.getChildViewHolder(view));
87690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            }
87790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
87890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar
87990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        public void onDetached(RecyclerView recyclerView) {
88090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            validateRemaining(recyclerView);
88190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
88290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar    }
8837a59e4b36500bcfddd842376d6c3abe0d9f23d84Yigit Boyar}
884