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
19754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertEquals;
20754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertFalse;
21754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertNotNull;
22754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertNotSame;
23754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertNull;
24754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertSame;
25754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertTrue;
26754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.fail;
270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
285a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyarimport android.graphics.Rect;
294c74dbe97fdd0d0ca7848bdc3a6a037b2a8d851bYigit Boyarimport android.os.Build;
304143554adb9b31b700b6876a251a64419e6111e2Yigit Boyarimport android.support.annotation.NonNull;
3135232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikasimport android.support.test.filters.LargeTest;
324c74dbe97fdd0d0ca7848bdc3a6a037b2a8d851bYigit Boyarimport android.support.test.filters.SdkSuppress;
330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.test.runner.AndroidJUnit4;
34f3e710cfe6ee7027d5ad5b5b55fe00cff4136de8Yigit Boyarimport android.support.v4.view.ViewCompat;
35b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.util.Log;
36b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.view.View;
37504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup;
38b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
39754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport org.hamcrest.CoreMatchers;
40754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport org.hamcrest.MatcherAssert;
41754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport org.junit.Test;
42754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport org.junit.runner.RunWith;
43754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikas
4411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.ArrayList;
458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport java.util.HashMap;
4611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.HashSet;
4711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.List;
4811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Map;
4911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Set;
50b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyarimport java.util.concurrent.atomic.AtomicBoolean;
51d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
52d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
53121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar/**
54121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar * Tests for {@link SimpleItemAnimator} API.
55121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar */
5635232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas@LargeTest
570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@RunWith(AndroidJUnit4.class)
58121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyarpublic class RecyclerViewAnimationsTest extends BaseRecyclerViewAnimationsTest {
59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
609051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    final List<TestViewHolder> recycledVHs = new ArrayList<>();
619051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
63e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar    public void keepFocusAfterChangeAnimation() throws Throwable {
64e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        setupBasic(10, 0, 5, new TestAdapter(10) {
65e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar            @Override
66e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
67e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar                    int position) {
68e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar                super.onBindViewHolder(holder, position);
69e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar                holder.itemView.setFocusableInTouchMode(true);
70e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar            }
71e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        });
72e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        ((SimpleItemAnimator)(mRecyclerView.getItemAnimator())).setSupportsChangeAnimations(true);
73e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar
74e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        final RecyclerView.ViewHolder oldVh = mRecyclerView.findViewHolderForAdapterPosition(3);
75e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        assertNotNull("test sanity", oldVh);
7642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
77e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar            @Override
78e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar            public void run() {
79e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar                oldVh.itemView.requestFocus();
80e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar            }
81e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        });
82e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        assertTrue("test sanity", oldVh.itemView.hasFocus());
83e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        mLayoutManager.expectLayouts(2);
84e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        mTestAdapter.changeAndNotify(3, 1);
85e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        mLayoutManager.waitForLayout(2);
86e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar
87e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        RecyclerView.ViewHolder newVh = mRecyclerView.findViewHolderForAdapterPosition(3);
88e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        assertNotNull("test sanity", newVh);
89e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        assertNotSame(oldVh, newVh);
90e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        assertFalse(oldVh.itemView.hasFocus());
91e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar        assertTrue(newVh.itemView.hasFocus());
92e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar    }
93e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar
94e3b89d11cf08a0e9aaa8162757246f286c6df2f8Yigit Boyar    @Test
9513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    public void changeAndDisappearDontReUseViewHolder() throws Throwable {
9613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        changeAndDisappearTest(false, false);
9713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    }
9813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
9913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    @Test
10013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    public void changeAndDisappearReUseViewHolder() throws Throwable {
10113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        changeAndDisappearTest(true, false);
10213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    }
10313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
10413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    @Test
10513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    public void changeAndDisappearReUseWithScrapViewHolder() throws Throwable {
10613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        changeAndDisappearTest(true, true);
10713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    }
10813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
10913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    public void changeAndDisappearTest(final boolean reUse, final boolean useScrap)
11013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            throws Throwable {
11113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        final List<RecyclerView.ViewHolder> mRecycled = new ArrayList<>();
11213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        final TestAdapter adapter = new TestAdapter(1) {
11313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            @Override
11413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            public void onViewRecycled(TestViewHolder holder) {
11513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                super.onViewRecycled(holder);
11613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                mRecycled.add(holder);
11713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            }
11813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        };
11913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        setupBasic(1, 0, 1, adapter);
12013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        RecyclerView.ViewHolder vh = mRecyclerView.getChildViewHolder(mRecyclerView.getChildAt(0));
12113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        LoggingItemAnimator animator = new LoggingItemAnimator() {
12213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            @Override
12313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
12413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                                                     @NonNull List<Object> payloads) {
12513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                return reUse;
12613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            }
12713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        };
12813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        mRecyclerView.setItemAnimator(animator);
12913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        mLayoutManager.expectLayouts(2);
13013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        final RecyclerView.ViewHolder[] updatedVH = new RecyclerView.ViewHolder[1];
13142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
13213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            @Override
13313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            public void run() {
13413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                adapter.notifyItemChanged(0);
13513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
13613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                    @Override
13713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                    void doLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
13813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                                  RecyclerView.State state) {
13913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                        if (state.isPreLayout()) {
14013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            super.doLayout(recycler, lm, state);
14113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                        } else {
14213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            lm.detachAndScrapAttachedViews(recycler);
14313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            final View view;
14413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            if (reUse && useScrap) {
14513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                                view = recycler.getScrapViewAt(0);
14613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            } else {
14713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                                view = recycler.getViewForPosition(0);
14813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            }
14913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            updatedVH[0] = RecyclerView.getChildViewHolderInt(view);
15013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                            lm.addDisappearingView(view);
15113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                        }
15213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                    }
15313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                };
15413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            }
15513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        });
15613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        mLayoutManager.waitForLayout(2);
15713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
15813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        MatcherAssert.assertThat(animator.contains(vh, animator.mAnimateDisappearanceList),
159dca8e68e966915b8314095e71538d231a7eee575Yigit Boyar                CoreMatchers.is(reUse));
16013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        MatcherAssert.assertThat(animator.contains(vh, animator.mAnimateChangeList),
161dca8e68e966915b8314095e71538d231a7eee575Yigit Boyar                CoreMatchers.is(!reUse));
16213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        MatcherAssert.assertThat(animator.contains(updatedVH[0], animator.mAnimateChangeList),
163dca8e68e966915b8314095e71538d231a7eee575Yigit Boyar                CoreMatchers.is(!reUse));
16413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        MatcherAssert.assertThat(animator.contains(updatedVH[0],
165dca8e68e966915b8314095e71538d231a7eee575Yigit Boyar                animator.mAnimateDisappearanceList), CoreMatchers.is(reUse));
16613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        waitForAnimations(10);
16713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        MatcherAssert.assertThat(mRecyclerView.getChildCount(), CoreMatchers.is(0));
16813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        if (useScrap || !reUse) {
16913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            MatcherAssert.assertThat(mRecycled.contains(vh), CoreMatchers.is(true));
17013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        } else {
17113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            MatcherAssert.assertThat(mRecyclerView.mRecycler.mCachedViews.contains(vh),
17213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                    CoreMatchers.is(true));
17313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        }
17413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
17513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        if (!reUse) {
17613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            MatcherAssert.assertThat(mRecycled.contains(updatedVH[0]), CoreMatchers.is(false));
17713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar            MatcherAssert.assertThat(mRecyclerView.mRecycler.mCachedViews.contains(updatedVH[0]),
17813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar                    CoreMatchers.is(true));
17913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar        }
18013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    }
18113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar
18213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar    @Test
1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void detectStableIdError() throws Throwable {
18466826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        setIgnoreMainThreadException(true);
185b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        final AtomicBoolean useBadIds = new AtomicBoolean(false);
186b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        TestAdapter adapter = new TestAdapter(10) {
187b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar            @Override
188b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar            public long getItemId(int position) {
189b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar                if (useBadIds.get() && position == 5) {
190b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar                    return super.getItemId(position) - 1;
191b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar                }
192b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar                return super.getItemId(position);
193b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar            }
194b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar
195b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar            @Override
196b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar            public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
197b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar                // ignore validation
198b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar            }
199b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        };
200b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        adapter.setHasStableIds(true);
201b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        setupBasic(10, 0, 10, adapter);
202b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        mLayoutManager.expectLayouts(2);
203b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        useBadIds.set(true);
204b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        adapter.changeAndNotify(4, 2);
205b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        mLayoutManager.waitForLayout(2);
20666826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        assertTrue(getMainThreadException() instanceof IllegalStateException);
20766826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        assertTrue(getMainThreadException().getMessage()
208b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar                .contains("Two different ViewHolders have the same stable ID."));
209b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        // TODO don't use this after moving this class to Junit 4
210b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        try {
211b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar            removeRecyclerView();
212b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar        } catch (Throwable t){}
213b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar    }
214b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar
215b2a6ee1e0b6c4343a52b7e176c29d0cfebcd1617Yigit Boyar
2160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dontLayoutReusedViewWithoutPredictive() throws Throwable {
2189051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
2199051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
2209051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
2219051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    final TestViewHolder target) throws Throwable {
2229051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
2239051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
2249051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
2259051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
2269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
2279051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void beforePreLayout(RecyclerView.Recycler recycler,
2289051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager lm, RecyclerView.State state) {
2299051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        fail("pre layout is not expected");
2309051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
2319051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
2329051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
2339051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void beforePostLayout(RecyclerView.Recycler recycler,
2349051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager layoutManager,
2359051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            RecyclerView.State state) {
2369051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        mLayoutItemCount = 7;
2379051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        View targetView = recycler
2389051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                                .getViewForPosition(target.getAdapterPosition());
2399051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        assertSame(targetView, target.itemView);
2409051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        super.beforePostLayout(recycler, layoutManager, state);
2419051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
2429051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
2439051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
2449051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void afterPostLayout(RecyclerView.Recycler recycler,
2459051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager layoutManager,
2469051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            RecyclerView.State state) {
2479051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        super.afterPostLayout(recycler, layoutManager, state);
2489051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        assertNull("test sanity. this view should not be re-laid out in post "
2499051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                                + "layout", target.itemView.getParent());
2509051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
2519051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                };
2529051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(1);
2539051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.requestSimpleAnimationsInNextLayout();
2549051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                requestLayoutOnUIThread(mRecyclerView);
2559051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
2569051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                checkForMainThreadException();
2579051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
2589051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateChangeList));
2599051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
2609051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // This is a LayoutManager problem if it asked for the view but didn't properly
2619051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // lay it out. It will move to disappearance
2629051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
2639051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
2649051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(recycledVHs.contains(target));
2659051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
2669051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
2679051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
2689051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
2690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dontLayoutReusedViewWithPredictive() throws Throwable {
2719051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
2729051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
2739051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
2749051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    final TestViewHolder target) throws Throwable {
2759051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
2769051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
2779051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
2789051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
2799051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
2809051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void beforePreLayout(RecyclerView.Recycler recycler,
2819051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager lm, RecyclerView.State state) {
2829051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        mLayoutItemCount = 9;
2839051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        super.beforePreLayout(recycler, lm, state);
2849051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
2859051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
2869051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
2879051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void beforePostLayout(RecyclerView.Recycler recycler,
2889051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager layoutManager,
2899051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            RecyclerView.State state) {
2909051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        mLayoutItemCount = 7;
2919051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        super.beforePostLayout(recycler, layoutManager, state);
2929051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
2939051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
2949051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
2959051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void afterPostLayout(RecyclerView.Recycler recycler,
2969051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager layoutManager,
2979051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            RecyclerView.State state) {
2989051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        super.afterPostLayout(recycler, layoutManager, state);
2999051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        assertNull("test sanity. this view should not be re-laid out in post "
3009051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                                + "layout", target.itemView.getParent());
3019051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
3029051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                };
3039051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(2);
3049051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mTestAdapter.deleteAndNotify(1, 1);
3059051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
3069051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                checkForMainThreadException();
3079051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
3089051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateChangeList));
3099051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
3109051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // This is a LayoutManager problem if it asked for the view but didn't properly
3119051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // lay it out. It will move to disappearance.
3129051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
3139051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
3149051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(recycledVHs.contains(target));
3159051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
3169051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
3179051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
3189051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
3190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
3200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void reuseHiddenViewWithoutPredictive() throws Throwable {
3219051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
3229051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
3239051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
3249051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    TestViewHolder target) throws Throwable {
3259051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
3269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
3279051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
3289051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(1);
3299051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.requestSimpleAnimationsInNextLayout();
3309051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 9;
3319051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                requestLayoutOnUIThread(mRecyclerView);
3329051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
3339051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
3349051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
3359051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateChangeList));
3369051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
3379051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
3389051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(recycledVHs.contains(target));
3399051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
3409051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
3419051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
3429051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
3430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
3440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void reuseHiddenViewWithoutAnimations() throws Throwable {
3459051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
3469051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
3479051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
3489051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    TestViewHolder target) throws Throwable {
3499051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
3509051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
3519051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
3529051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(1);
3539051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 9;
3549051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                requestLayoutOnUIThread(mRecyclerView);
3559051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
3569051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
3579051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
3589051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateChangeList));
3599051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
3609051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
3619051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(recycledVHs.contains(target));
3629051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
3639051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
3649051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
3659051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
3660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
3670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void reuseHiddenViewWithPredictive() throws Throwable {
3689051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
3699051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
3709051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
3719051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    TestViewHolder target) throws Throwable {
3729051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // it should move to change scrap and then show up from there
3739051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
3749051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
3759051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
3769051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(2);
3779051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mTestAdapter.deleteAndNotify(2, 1);
3789051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
3799051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
3809051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // This LM does not layout the additional item so it does predictive wrong.
3819051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // We should still handle it and animate persistence for this item
3829051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
3839051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateChangeList));
3849051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
3859051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
3869051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.mMoveVHs.contains(target));
3879051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(recycledVHs.contains(target));
3889051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
3899051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
3909051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
3919051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
3920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
3930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void reuseHiddenViewWithProperPredictive() throws Throwable {
3949051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
3959051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
3969051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
3979051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    TestViewHolder target) throws Throwable {
3989051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // it should move to change scrap and then show up from there
3999051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
4009051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
4019051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
4029051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
4039051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
4049051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void beforePreLayout(RecyclerView.Recycler recycler,
4059051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager lm, RecyclerView.State state) {
4069051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        mLayoutItemCount = 9;
4079051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        super.beforePreLayout(recycler, lm, state);
4089051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
4099051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
4109051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
4119051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void afterPreLayout(RecyclerView.Recycler recycler,
4129051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            AnimationLayoutManager layoutManager,
4139051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            RecyclerView.State state) {
4149051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        mLayoutItemCount = 8;
4159051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        super.afterPreLayout(recycler, layoutManager, state);
4169051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
4179051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                };
4189051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
4199051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(2);
4209051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mTestAdapter.deleteAndNotify(2, 1);
4219051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
4229051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
4239051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // This LM implements predictive animations properly by requesting target view
4249051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // in pre-layout.
4259051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
4269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateChangeList));
4279051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
4289051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
4299051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.mMoveVHs.contains(target));
4309051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(recycledVHs.contains(target));
4319051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
4329051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
4339051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
4349051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
435ad91601c2d79f3a5b5124c617027e09e89d84a1eAurimas Liutikas    // Disable this test on ICS because it causes testing devices to freeze.
436ad91601c2d79f3a5b5124c617027e09e89d84a1eAurimas Liutikas    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
4370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dontReuseHiddenViewOnInvalidate() throws Throwable {
4399051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
4409051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
4419051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
4429051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    TestViewHolder target) throws Throwable {
4439051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // it should move to change scrap and then show up from there
4449051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
4459051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
4469051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
4479051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(1);
4489051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mTestAdapter.dispatchDataSetChanged();
4499051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
4509051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
4519051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(mRecyclerView.getItemAnimator().isRunning());
4529051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
4539051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateChangeList));
4549051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
4559051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
4569051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(recycledVHs.contains(target));
4579051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
4589051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
4599051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
4609051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
4610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dontReuseOnTypeChange() throws Throwable {
4639051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        reuseHiddenViewTest(new ReuseTestCallback() {
4649051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
4659051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void postSetup(List<TestViewHolder> recycledList,
4669051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    TestViewHolder target) throws Throwable {
4679051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                // it should move to change scrap and then show up from there
4689051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                LoggingItemAnimator itemAnimator = (LoggingItemAnimator) mRecyclerView
4699051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        .getItemAnimator();
4709051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                itemAnimator.reset();
4719051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.expectLayouts(1);
4729051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                target.mBoundItem.mType += 2;
4739051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 9;
4749051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mTestAdapter.changeAndNotify(target.getAdapterPosition(), 1);
4759051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                requestLayoutOnUIThread(mRecyclerView);
4769051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                mLayoutManager.waitForLayout(2);
4779051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
4789051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(itemAnimator.mChangeOldVHs.contains(target));
4799051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimatePersistenceList));
4809051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateAppearanceList));
4819051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(itemAnimator.contains(target, itemAnimator.mAnimateDisappearanceList));
4829051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(mRecyclerView.mChildHelper.isHidden(target.itemView));
4839051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertFalse(recycledVHs.contains(target));
4849051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                waitForAnimations(5);
4859051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                assertTrue(recycledVHs.contains(target));
4869051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
4879051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        });
4889051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
4899051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
4909051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    interface ReuseTestCallback {
4919051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
4929051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        void postSetup(List<TestViewHolder> recycledList, TestViewHolder target) throws Throwable;
4939051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
4949051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
4959051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    @Override
4969051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    protected RecyclerView.ItemAnimator createItemAnimator() {
4979051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        return new LoggingItemAnimator();
4989051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
4999051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
5009051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    public void reuseHiddenViewTest(ReuseTestCallback callback) throws Throwable {
5019051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        TestAdapter adapter = new TestAdapter(10) {
5029051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            @Override
5039051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            public void onViewRecycled(TestViewHolder holder) {
5049051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                super.onViewRecycled(holder);
5059051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                recycledVHs.add(holder);
5069051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            }
5079051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        };
5089051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        setupBasic(10, 0, 10, adapter);
5099051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mRecyclerView.setItemViewCacheSize(0);
5109051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        TestViewHolder target = (TestViewHolder) mRecyclerView.findViewHolderForAdapterPosition(9);
5119051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mRecyclerView.getItemAnimator().setAddDuration(1000);
5129051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mRecyclerView.getItemAnimator().setRemoveDuration(1000);
5139051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mRecyclerView.getItemAnimator().setChangeDuration(1000);
5149051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mRecyclerView.getItemAnimator().setMoveDuration(1000);
5159051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 8;
5169051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mLayoutManager.expectLayouts(2);
5179051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        adapter.deleteAndNotify(2, 1);
5189051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mLayoutManager.waitForLayout(2);
5199051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        // test sanity, make sure target is hidden now
5209051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        assertTrue("test sanity", mRecyclerView.mChildHelper.isHidden(target.itemView));
5219051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        callback.postSetup(recycledVHs, target);
5229051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        // TODO TEST ITEM INVALIDATION OR TYPE CHANGE IN BETWEEN
5239051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        // TODO TEST ITEM IS RECEIVED FROM RECYCLER BUT NOT RE-ADDED
5249051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        // TODO TEST ITEM ANIMATOR IS CALLED TO GET NEW INFORMATION ABOUT LOCATION
5259051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
5269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
5279051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
5280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
5290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void detachBeforeAnimations() throws Throwable {
5300ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        setupBasic(10, 0, 5);
5310ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        final RecyclerView rv = mRecyclerView;
5320ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        waitForAnimations(2);
5330ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        final DefaultItemAnimator animator = new DefaultItemAnimator() {
5340ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar            @Override
5350ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar            public void runPendingAnimations() {
5360ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar                super.runPendingAnimations();
5370ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar            }
5380ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        };
5390ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        rv.setItemAnimator(animator);
5400ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        mLayoutManager.expectLayouts(2);
5410ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        mTestAdapter.deleteAndNotify(3, 4);
5420ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        mLayoutManager.waitForLayout(2);
5430ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        removeRecyclerView();
5440ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        assertNull("test sanity check RV should be removed", rv.getParent());
5450ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        assertEquals("no views should be hidden", 0, rv.mChildHelper.mHiddenViews.size());
5460ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        assertFalse("there should not be any animations running", animator.isRunning());
5470ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar    }
5480ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar
5490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
5500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void moveDeleted() throws Throwable {
5515a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        setupBasic(4, 0, 3);
5525a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        waitForAnimations(2);
5535a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        final View[] targetChild = new View[1];
5545a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        final LoggingItemAnimator animator = new LoggingItemAnimator();
55542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
5565a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar            @Override
5575a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar            public void run() {
5585a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                mRecyclerView.setItemAnimator(animator);
5595a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                targetChild[0] = mRecyclerView.getChildAt(1);
5605a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar            }
5615a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        });
5625a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar
5635a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        assertNotNull("test sanity", targetChild);
5645a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        mLayoutManager.expectLayouts(1);
56542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
5665a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar            @Override
5675a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar            public void run() {
5685a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
5695a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                    @Override
5705a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
5719051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            RecyclerView.State state) {
5725a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                        if (view == targetChild[0]) {
5735a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                            outRect.set(10, 20, 30, 40);
5745a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                        } else {
5755a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                            outRect.set(0, 0, 0, 0);
5765a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                        }
5775a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                    }
5785a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                });
5795a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar            }
5805a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        });
5815a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        mLayoutManager.waitForLayout(1);
5825a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar
5835a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        // now delete that item.
5845a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        mLayoutManager.expectLayouts(2);
5855a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        RecyclerView.ViewHolder targetVH = mRecyclerView.getChildViewHolder(targetChild[0]);
5865a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        targetChild[0] = null;
5875a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        mTestAdapter.deleteAndNotify(1, 1);
5885a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        mLayoutManager.waitForLayout(2);
5895a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        assertFalse("if deleted view moves, it should not be in move animations",
5905a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar                animator.mMoveVHs.contains(targetVH));
5915a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        assertEquals("only 1 item is deleted", 1, animator.mRemoveVHs.size());
5925a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        assertTrue("the target view is removed", animator.mRemoveVHs.contains(targetVH
5935a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar        ));
5945a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar    }
5955a9366fba5e26329bc9a988cfd2d43df980f2764Yigit Boyar
596344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    private void runTestImportantForAccessibilityWhileDeteling(
597344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            final int boundImportantForAccessibility,
598344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            final int expectedImportantForAccessibility) throws Throwable {
599344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        // Adapter binding the item to the initial accessibility option.
600344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        // RecyclerView is expected to change it to 'expectedImportantForAccessibility'.
601344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        TestAdapter adapter = new TestAdapter(1) {
602344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            @Override
603344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            public void onBindViewHolder(TestViewHolder holder, int position) {
604344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                super.onBindViewHolder(holder, position);
605344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.setImportantForAccessibility(
606344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        holder.itemView, boundImportantForAccessibility);
607344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            }
608344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        };
609344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
610344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        // Set up with 1 item.
611344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        setupBasic(1, 0, 1, adapter);
612344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        waitForAnimations(2);
613344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        final View[] targetChild = new View[1];
614344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        final LoggingItemAnimator animator = new LoggingItemAnimator();
615344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        animator.setRemoveDuration(500);
61642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
617344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            @Override
618344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            public void run() {
619344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                mRecyclerView.setItemAnimator(animator);
620344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                targetChild[0] = mRecyclerView.getChildAt(0);
621344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                assertEquals(
622344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        expectedImportantForAccessibility,
623344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        ViewCompat.getImportantForAccessibility(targetChild[0]));
624344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            }
625344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        });
626344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
627344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        assertNotNull("test sanity", targetChild[0]);
628344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
629344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        // now delete that item.
630344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mLayoutManager.expectLayouts(2);
631344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mTestAdapter.deleteAndNotify(0, 1);
632344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
633344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mLayoutManager.waitForLayout(2);
634344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
63542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
636344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            @Override
637344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            public void run() {
638344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                // The view is still a child of mRecyclerView, and is invisible for accessibility.
639344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                assertTrue(targetChild[0].getParent() == mRecyclerView);
640344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                assertEquals(
641344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
642344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        ViewCompat.getImportantForAccessibility(targetChild[0]));
643344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            }
644344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        });
645344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
646344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        waitForAnimations(2);
647344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
648344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        // Delete animation is now complete.
64942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
650344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            @Override
651344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            public void run() {
652344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                // The view is in recycled state, and back to the expected accessibility.
653344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                assertTrue(targetChild[0].getParent() == null);
654344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                assertEquals(
655344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        expectedImportantForAccessibility,
656344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        ViewCompat.getImportantForAccessibility(targetChild[0]));
657344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            }
658344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        });
659344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
660344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        // Add 1 element, which should use same view.
661344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mLayoutManager.expectLayouts(2);
662344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mTestAdapter.addAndNotify(1);
663344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mLayoutManager.waitForLayout(2);
664344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
66542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
666344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            @Override
667344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            public void run() {
668344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                // The view should be reused, and have the expected accessibility.
669344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                assertTrue(
670344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        "the item must be reused", targetChild[0] == mRecyclerView.getChildAt(0));
671344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                assertEquals(
672344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        expectedImportantForAccessibility,
673344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                        ViewCompat.getImportantForAccessibility(targetChild[0]));
674344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            }
675344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        });
676344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    }
677344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
6780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
6794c74dbe97fdd0d0ca7848bdc3a6a037b2a8d851bYigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
6800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void importantForAccessibilityWhileDetelingAuto() throws Throwable {
681344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        runTestImportantForAccessibilityWhileDeteling(
682344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO,
683344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
684344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    }
685344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
6860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
6874c74dbe97fdd0d0ca7848bdc3a6a037b2a8d851bYigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
6880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void importantForAccessibilityWhileDetelingNo() throws Throwable {
689344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        runTestImportantForAccessibilityWhileDeteling(
690344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO,
691344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);
692344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    }
693344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
6940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
6954c74dbe97fdd0d0ca7848bdc3a6a037b2a8d851bYigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
6960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void importantForAccessibilityWhileDetelingNoHideDescandants() throws Throwable {
697344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        runTestImportantForAccessibilityWhileDeteling(
698344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
699344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
700344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    }
701344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
7020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
7034c74dbe97fdd0d0ca7848bdc3a6a037b2a8d851bYigit Boyar    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
7040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void importantForAccessibilityWhileDetelingYes() throws Throwable {
705344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        runTestImportantForAccessibilityWhileDeteling(
706344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES,
707344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
708344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    }
709344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
7100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
7110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void preLayoutPositionCleanup() throws Throwable {
71242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        setupBasic(4, 0, 4);
71342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mLayoutManager.expectLayouts(2);
71442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
71542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            @Override
71642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            void beforePreLayout(RecyclerView.Recycler recycler,
71742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
71842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutMin = 0;
71942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutItemCount = 3;
72042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            }
72142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
72242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            @Override
72342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            void beforePostLayout(RecyclerView.Recycler recycler,
72442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    AnimationLayoutManager layoutManager,
72542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    RecyclerView.State state) {
72642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutMin = 0;
72742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutItemCount = 4;
72842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            }
72942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        };
73042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mTestAdapter.addAndNotify(0, 1);
73142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mLayoutManager.waitForLayout(2);
73242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
73342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
73442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar    }
73542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
7360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
7370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addRemoveSamePass() throws Throwable {
73893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final List<RecyclerView.ViewHolder> mRecycledViews
73993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
74093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        TestAdapter adapter = new TestAdapter(50) {
74193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
74293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public void onViewRecycled(TestViewHolder holder) {
74393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                super.onViewRecycled(holder);
74493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                mRecycledViews.add(holder);
74593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
74693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        };
74793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        adapter.setHasStableIds(true);
74893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        setupBasic(50, 3, 5, adapter);
74993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mRecyclerView.setItemViewCacheSize(0);
75093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final ArrayList<RecyclerView.ViewHolder> addVH
75193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
75293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final ArrayList<RecyclerView.ViewHolder> removeVH
75393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
75493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
75593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final ArrayList<RecyclerView.ViewHolder> moveVH
75693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
75793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
75893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final View[] testView = new View[1];
75993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
76093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
76193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public boolean animateAdd(RecyclerView.ViewHolder holder) {
76293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                addVH.add(holder);
76393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                return true;
76493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
76593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
76693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
76793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public boolean animateRemove(RecyclerView.ViewHolder holder) {
76893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                removeVH.add(holder);
76993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                return true;
77093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
77193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
77293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
77393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY,
77493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    int toX, int toY) {
77593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                moveVH.add(holder);
77693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                return true;
77793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
77893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        });
77993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
78093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
78193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            void afterPreLayout(RecyclerView.Recycler recycler,
78293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    AnimationLayoutManager layoutManager,
78393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    RecyclerView.State state) {
78493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                super.afterPreLayout(recycler, layoutManager, state);
78593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0] = recycler.getViewForPosition(45);
78693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0].measure(View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST),
78793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST));
78893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0].layout(10, 10, 10 + testView[0].getMeasuredWidth(),
78993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                        10 + testView[0].getMeasuredHeight());
79093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                layoutManager.addView(testView[0], 4);
79193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
79293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
79393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
79493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            void afterPostLayout(RecyclerView.Recycler recycler,
79593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    AnimationLayoutManager layoutManager,
79693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    RecyclerView.State state) {
79793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                super.afterPostLayout(recycler, layoutManager, state);
79893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0].layout(50, 50, 50 + testView[0].getMeasuredWidth(),
79993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                        50 + testView[0].getMeasuredHeight());
80093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                layoutManager.addDisappearingView(testView[0], 4);
80193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
80293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        };
80393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 3;
80493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 5;
80593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mRecycledViews.clear();
80693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.expectLayouts(2);
80793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mTestAdapter.deleteAndNotify(3, 1);
80893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.waitForLayout(2);
80993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
81093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : addVH) {
81193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            assertNotSame("add-remove item should not animate add", testView[0], vh.itemView);
81293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
81393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : moveVH) {
81493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            assertNotSame("add-remove item should not animate move", testView[0], vh.itemView);
81593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
81693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : removeVH) {
81793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            assertNotSame("add-remove item should not animate remove", testView[0], vh.itemView);
81893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
81993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        boolean found = false;
82093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : mRecycledViews) {
82193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            found |= vh.itemView == testView[0];
82293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
82393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        assertTrue("added-removed view should be recycled", found);
82493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar    }
82593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
8260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
8270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void tmpRemoveMe() throws Throwable {
828121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar        changeAnimTest(false, false, true, false);
829121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar    }
830121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar
8310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
8320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void changeAnimations() throws Throwable {
8337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final boolean[] booleans = {true, false};
8347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        for (boolean supportsChange : booleans) {
8357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            for (boolean changeType : booleans) {
8367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                for (boolean hasStableIds : booleans) {
8377c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    for (boolean deleteSomeItems : booleans) {
8387c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        changeAnimTest(supportsChange, changeType, hasStableIds, deleteSomeItems);
8397c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    }
8407c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    removeRecyclerView();
8417c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
8427c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
8437c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
8447c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    }
845121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar
8467c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    public void changeAnimTest(final boolean supportsChangeAnim, final boolean changeType,
8479051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            final boolean hasStableIds, final boolean deleteSomeItems) throws Throwable {
8487c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final int changedIndex = 3;
8497c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final int defaultType = 1;
8507c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final AtomicInteger changedIndexNewType = new AtomicInteger(defaultType);
8517c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final String logPrefix = "supportsChangeAnim:" + supportsChangeAnim +
8527c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", change view type:" + changeType +
8537c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", has stable ids:" + hasStableIds +
854121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                ", delete some items:" + deleteSomeItems;
8557c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        TestAdapter testAdapter = new TestAdapter(10) {
8567c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
8577c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public int getItemViewType(int position) {
8587c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                return position == changedIndex ? changedIndexNewType.get() : defaultType;
8597c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
8607c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
8617c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
8627c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public TestViewHolder onCreateViewHolder(ViewGroup parent,
8637c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    int viewType) {
8647c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
8657c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (DEBUG) {
8667c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    Log.d(TAG, logPrefix + " onCreateVH" + vh.toString());
8677c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
8687c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                return vh;
8697c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
8707c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
8717c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
8727c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public void onBindViewHolder(TestViewHolder holder,
8737c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    int position) {
8747c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                super.onBindViewHolder(holder, position);
8757c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (DEBUG) {
8767c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    Log.d(TAG, logPrefix + " onBind to " + position + "" + holder.toString());
8777c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
8787c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
8797c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        };
8807c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        testAdapter.setHasStableIds(hasStableIds);
8817c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter);
8829051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        ((SimpleItemAnimator) mRecyclerView.getItemAnimator()).setSupportsChangeAnimations(
883121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                supportsChangeAnim);
8847c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
8857c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final RecyclerView.ViewHolder toBeChangedVH =
886115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                mRecyclerView.findViewHolderForLayoutPosition(changedIndex);
8877c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
8887c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
8897c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            void afterPreLayout(RecyclerView.Recycler recycler,
8907c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    AnimationLayoutManager layoutManager,
8917c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    RecyclerView.State state) {
892115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(
893115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        changedIndex);
894121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                assertTrue(logPrefix + " changed view holder should have correct flag"
895121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                        , vh.isUpdated());
8967c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
8977c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
8987c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
8997c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            void afterPostLayout(RecyclerView.Recycler recycler,
9007c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    AnimationLayoutManager layoutManager, RecyclerView.State state) {
901115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(
902115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        changedIndex);
9037c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (supportsChangeAnim) {
9047c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertNotSame(logPrefix + "a new VH should be given if change is supported",
9057c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            toBeChangedVH, vh);
9067c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                } else if (!changeType && hasStableIds) {
9077c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertSame(logPrefix + "if change animations are not supported but we have "
9087c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            + "stable ids, same view holder should be returned", toBeChangedVH, vh);
9097c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
9107c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                super.beforePostLayout(recycler, layoutManager, state);
9117c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
9127c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        };
9137c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.expectLayouts(1);
9147c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        if (changeType) {
9157c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            changedIndexNewType.set(defaultType + 1);
9167c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
9177c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        if (deleteSomeItems) {
91842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas            mActivityRule.runOnUiThread(new Runnable() {
9197c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                @Override
9207c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                public void run() {
9217c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    try {
9227c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        mTestAdapter.deleteAndNotify(changedIndex + 2, 1);
9237c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        mTestAdapter.notifyItemChanged(3);
9247c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    } catch (Throwable throwable) {
9257c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        throwable.printStackTrace();
9267c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    }
9277c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
9287c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
9297c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            });
9307c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        } else {
931121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar            mTestAdapter.changeAndNotify(3, 1);
9327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
9337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
9347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.waitForLayout(2);
9357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    }
9367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
9374143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    private void testChangeWithPayload(final boolean supportsChangeAnim,
9384143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar            final boolean canReUse, Object[][] notifyPayloads, Object[][] expectedPayloadsInOnBind)
9399051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            throws Throwable {
94021b345f101abc385496f42d250e580d21f1287b6Dake Gu        final List<Object> expectedPayloads = new ArrayList<Object>();
94121b345f101abc385496f42d250e580d21f1287b6Dake Gu        final int changedIndex = 3;
94221b345f101abc385496f42d250e580d21f1287b6Dake Gu        TestAdapter testAdapter = new TestAdapter(10) {
94321b345f101abc385496f42d250e580d21f1287b6Dake Gu            @Override
94421b345f101abc385496f42d250e580d21f1287b6Dake Gu            public int getItemViewType(int position) {
94521b345f101abc385496f42d250e580d21f1287b6Dake Gu                return 1;
94621b345f101abc385496f42d250e580d21f1287b6Dake Gu            }
94721b345f101abc385496f42d250e580d21f1287b6Dake Gu
94821b345f101abc385496f42d250e580d21f1287b6Dake Gu            @Override
94921b345f101abc385496f42d250e580d21f1287b6Dake Gu            public TestViewHolder onCreateViewHolder(ViewGroup parent,
95021b345f101abc385496f42d250e580d21f1287b6Dake Gu                    int viewType) {
95121b345f101abc385496f42d250e580d21f1287b6Dake Gu                TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
95221b345f101abc385496f42d250e580d21f1287b6Dake Gu                if (DEBUG) {
95321b345f101abc385496f42d250e580d21f1287b6Dake Gu                    Log.d(TAG, " onCreateVH" + vh.toString());
95421b345f101abc385496f42d250e580d21f1287b6Dake Gu                }
95521b345f101abc385496f42d250e580d21f1287b6Dake Gu                return vh;
95621b345f101abc385496f42d250e580d21f1287b6Dake Gu            }
95721b345f101abc385496f42d250e580d21f1287b6Dake Gu
95821b345f101abc385496f42d250e580d21f1287b6Dake Gu            @Override
95921b345f101abc385496f42d250e580d21f1287b6Dake Gu            public void onBindViewHolder(TestViewHolder holder,
96021b345f101abc385496f42d250e580d21f1287b6Dake Gu                    int position, List<Object> payloads) {
96121b345f101abc385496f42d250e580d21f1287b6Dake Gu                super.onBindViewHolder(holder, position);
96221b345f101abc385496f42d250e580d21f1287b6Dake Gu                if (DEBUG) {
96321b345f101abc385496f42d250e580d21f1287b6Dake Gu                    Log.d(TAG, " onBind to " + position + "" + holder.toString());
96421b345f101abc385496f42d250e580d21f1287b6Dake Gu                }
965f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                assertEquals(expectedPayloads, payloads);
96621b345f101abc385496f42d250e580d21f1287b6Dake Gu            }
96721b345f101abc385496f42d250e580d21f1287b6Dake Gu        };
96821b345f101abc385496f42d250e580d21f1287b6Dake Gu        testAdapter.setHasStableIds(false);
96921b345f101abc385496f42d250e580d21f1287b6Dake Gu        setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter);
970f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar        mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
971f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar            @Override
972f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar            public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
973f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                    @NonNull List<Object> payloads) {
974f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                return canReUse && super.canReuseUpdatedViewHolder(viewHolder, payloads);
975f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar            }
976f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar        });
9779051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        ((SimpleItemAnimator) mRecyclerView.getItemAnimator()).setSupportsChangeAnimations(
978121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                supportsChangeAnim);
97921b345f101abc385496f42d250e580d21f1287b6Dake Gu
98021b345f101abc385496f42d250e580d21f1287b6Dake Gu        int numTests = notifyPayloads.length;
9819051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        for (int i = 0; i < numTests; i++) {
98221b345f101abc385496f42d250e580d21f1287b6Dake Gu            mLayoutManager.expectLayouts(1);
98321b345f101abc385496f42d250e580d21f1287b6Dake Gu            expectedPayloads.clear();
98421b345f101abc385496f42d250e580d21f1287b6Dake Gu            for (int j = 0; j < expectedPayloadsInOnBind[i].length; j++) {
98521b345f101abc385496f42d250e580d21f1287b6Dake Gu                expectedPayloads.add(expectedPayloadsInOnBind[i][j]);
98621b345f101abc385496f42d250e580d21f1287b6Dake Gu            }
98721b345f101abc385496f42d250e580d21f1287b6Dake Gu            final Object[] payloadsToSend = notifyPayloads[i];
98842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas            mActivityRule.runOnUiThread(new Runnable() {
98921b345f101abc385496f42d250e580d21f1287b6Dake Gu                @Override
99021b345f101abc385496f42d250e580d21f1287b6Dake Gu                public void run() {
99121b345f101abc385496f42d250e580d21f1287b6Dake Gu                    for (int j = 0; j < payloadsToSend.length; j++) {
99221b345f101abc385496f42d250e580d21f1287b6Dake Gu                        mTestAdapter.notifyItemChanged(changedIndex, payloadsToSend[j]);
99321b345f101abc385496f42d250e580d21f1287b6Dake Gu                    }
99421b345f101abc385496f42d250e580d21f1287b6Dake Gu                }
99521b345f101abc385496f42d250e580d21f1287b6Dake Gu            });
99621b345f101abc385496f42d250e580d21f1287b6Dake Gu            mLayoutManager.waitForLayout(2);
9979051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            checkForMainThreadException();
99821b345f101abc385496f42d250e580d21f1287b6Dake Gu        }
99921b345f101abc385496f42d250e580d21f1287b6Dake Gu    }
100021b345f101abc385496f42d250e580d21f1287b6Dake Gu
10010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
10020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void crossFadingChangeAnimationWithPayload() throws Throwable {
100321b345f101abc385496f42d250e580d21f1287b6Dake Gu        // for crossfading change animation,  will receive EMPTY payload in onBindViewHolder
1004f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar        testChangeWithPayload(true, true,
1005f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                new Object[][]{
1006f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[]{"abc"},
1007f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[]{"abc", null, "cdf"},
1008f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[]{"abc", null},
1009f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[]{null, "abc"},
1010f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[]{"abc", "cdf"}
1011f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                },
1012f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                new Object[][]{
1013f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[]{"abc"},
1014f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[0],
1015f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[0],
1016f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[0],
1017f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                        new Object[]{"abc", "cdf"}
1018f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar                });
1019f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar    }
1020f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar
10210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
10220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void crossFadingChangeAnimationWithPayloadWithoutReuse() throws Throwable {
1023f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar        // for crossfading change animation,  will receive EMPTY payload in onBindViewHolder
1024f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar        testChangeWithPayload(true, false,
102521b345f101abc385496f42d250e580d21f1287b6Dake Gu                new Object[][]{
10269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc"},
10279051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc", null, "cdf"},
10289051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc", null},
10299051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{null, "abc"},
10309051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc", "cdf"}
103121b345f101abc385496f42d250e580d21f1287b6Dake Gu                },
103221b345f101abc385496f42d250e580d21f1287b6Dake Gu                new Object[][]{
10339051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0],
10349051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0],
10359051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0],
10369051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0],
10379051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0]
103821b345f101abc385496f42d250e580d21f1287b6Dake Gu                });
103921b345f101abc385496f42d250e580d21f1287b6Dake Gu    }
104021b345f101abc385496f42d250e580d21f1287b6Dake Gu
10410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
10420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void noChangeAnimationWithPayload() throws Throwable {
104321b345f101abc385496f42d250e580d21f1287b6Dake Gu        // for Change Animation disabled, payload should match the payloads unless
104421b345f101abc385496f42d250e580d21f1287b6Dake Gu        // null payload is fired.
1045f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar        testChangeWithPayload(false, true,
104621b345f101abc385496f42d250e580d21f1287b6Dake Gu                new Object[][]{
10479051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc"},
10489051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc", null, "cdf"},
10499051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc", null},
10509051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{null, "abc"},
10519051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc", "cdf"}
105221b345f101abc385496f42d250e580d21f1287b6Dake Gu                },
105321b345f101abc385496f42d250e580d21f1287b6Dake Gu                new Object[][]{
10549051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc"},
10559051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0],
10569051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0],
10579051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[0],
10589051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        new Object[]{"abc", "cdf"}
105921b345f101abc385496f42d250e580d21f1287b6Dake Gu                });
106021b345f101abc385496f42d250e580d21f1287b6Dake Gu    }
106121b345f101abc385496f42d250e580d21f1287b6Dake Gu
10620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
10630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleDuringAnimations() throws Throwable {
1064504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
1065504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(1000) {
1066504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
1067504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
1068504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
1069504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                childCount.incrementAndGet();
1070504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return super.onCreateViewHolder(parent, viewType);
1071504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
1072504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
1073504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        setupBasic(1000, 10, 20, adapter);
1074504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 10;
1075504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 20;
1076504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1077504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() {
1078504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
1079504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
1080504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
1081504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                childCount.decrementAndGet();
1082504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
1083504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1084504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
1085504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
1086504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
1087504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
1088504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    childCount.incrementAndGet();
1089504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
1090504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
1091504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
1092504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        });
1093504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
1094504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
10959051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        for (int i = 0; i < 100; i++) {
1096504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
1097504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(50);
1098504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
1099504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
1100504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
1101504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
1102504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
1103504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
1104504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
11050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
11060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void notifyDataSetChanged() throws Throwable {
1107b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        setupBasic(10, 3, 4);
1108b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        int layoutCount = mLayoutManager.mTotalLayoutCount;
1109b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.expectLayouts(1);
111042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
1111b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            @Override
1112b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            public void run() {
1113b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                try {
1114b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                    mTestAdapter.deleteAndNotify(4, 1);
1115cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    mTestAdapter.dispatchDataSetChanged();
1116b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                } catch (Throwable throwable) {
1117b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                    throwable.printStackTrace();
1118b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                }
1119b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar
1120b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            }
1121b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        });
1122b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.waitForLayout(2);
1123b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        getInstrumentation().waitForIdleSync();
1124b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        assertEquals("on notify data set changed, predictive animations should not run",
1125b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                layoutCount + 1, mLayoutManager.mTotalLayoutCount);
1126b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.expectLayouts(2);
1127b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mTestAdapter.addAndNotify(4, 2);
1128b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        // make sure animations recover
1129b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.waitForLayout(2);
1130b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar    }
1131b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar
11320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
11330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void stableIdNotifyDataSetChanged() throws Throwable {
1134e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final int itemCount = 20;
1135e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        List<Item> initialSet = new ArrayList<Item>();
1136e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final TestAdapter adapter = new TestAdapter(itemCount) {
1137e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
1138e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public long getItemId(int position) {
1139e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                return mItems.get(position).mId;
1140e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
1141e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        };
1142e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        adapter.setHasStableIds(true);
1143e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        initialSet.addAll(adapter.mItems);
1144e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        positionStatesTest(itemCount, 5, 5, adapter, new AdapterOps() {
11459051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
11469051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void onRun(TestAdapter testAdapter) throws Throwable {
11479051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        Item item5 = adapter.mItems.get(5);
11489051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        Item item6 = adapter.mItems.get(6);
11499051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        item5.mAdapterIndex = 6;
11509051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        item6.mAdapterIndex = 5;
11519051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        adapter.mItems.remove(5);
11529051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        adapter.mItems.add(6, item5);
11539051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        adapter.dispatchDataSetChanged();
11549051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        //hacky, we support only 1 layout pass
11559051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        mLayoutManager.layoutLatch.countDown();
11569051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
11579051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                }, PositionConstraint.scrap(6, -1, 5), PositionConstraint.scrap(5, -1, 6),
1158e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                PositionConstraint.scrap(7, -1, 7), PositionConstraint.scrap(8, -1, 8),
1159e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                PositionConstraint.scrap(9, -1, 9));
1160e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        // now mix items.
1161e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
1162e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
1163d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
11640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
11650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void getItemForDeletedView() throws Throwable {
11668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getItemForDeletedViewTest(false);
11678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getItemForDeletedViewTest(true);
116811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    }
116911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
11708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void getItemForDeletedViewTest(boolean stableIds) throws Throwable {
117111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final Set<Integer> itemViewTypeQueries = new HashSet<Integer>();
117211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final Set<Integer> itemIdQueries = new HashSet<Integer>();
117311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        TestAdapter adapter = new TestAdapter(10) {
117411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            @Override
117511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            public int getItemViewType(int position) {
117611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemViewTypeQueries.add(position);
117711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                return super.getItemViewType(position);
117811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
117911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
118011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            @Override
118111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            public long getItemId(int position) {
118211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemIdQueries.add(position);
118311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                return mItems.get(position).mId;
118411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
118511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        };
118611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        adapter.setHasStableIds(stableIds);
118711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        setupBasic(10, 0, 10, adapter);
118811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        assertEquals("getItemViewType for all items should be called", 10,
118911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemViewTypeQueries.size());
119011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        if (adapter.hasStableIds()) {
119111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertEquals("getItemId should be called when adapter has stable ids", 10,
119211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemIdQueries.size());
119311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        } else {
119411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertEquals("getItemId should not be called when adapter does not have stable ids", 0,
119511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemIdQueries.size());
119611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
119711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        itemViewTypeQueries.clear();
119811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        itemIdQueries.clear();
119911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        mLayoutManager.expectLayouts(2);
120011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        // delete last two
120111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final int deleteStart = 8;
120211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final int deleteCount = adapter.getItemCount() - deleteStart;
120311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        adapter.deleteAndNotify(deleteStart, deleteCount);
120411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        mLayoutManager.waitForLayout(2);
120511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        for (int i = 0; i < deleteStart; i++) {
120611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertTrue("getItemViewType for existing item " + i + " should be called",
120711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemViewTypeQueries.contains(i));
120811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            if (adapter.hasStableIds()) {
120911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                assertTrue("getItemId for existing item " + i
12109051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                                + " should be called when adapter has stable ids",
121111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        itemIdQueries.contains(i));
121211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
121311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
121411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        for (int i = deleteStart; i < deleteStart + deleteCount; i++) {
121511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertFalse("getItemViewType for deleted item " + i + " SHOULD NOT be called",
121611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemViewTypeQueries.contains(i));
121711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            if (adapter.hasStableIds()) {
12188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertFalse("getItemId for deleted item " + i + " SHOULD NOT be called",
121911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        itemIdQueries.contains(i));
122011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
122111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
122211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    }
122311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
12240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
12250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void deleteInvisibleMultiStep() throws Throwable {
12268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(1000, 1, 7);
12278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
12288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
12298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(1);
12308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // try to trigger race conditions
12318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int targetItemCount = mTestAdapter.getItemCount();
12328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < 100; i++) {
12338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});
123490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            checkForMainThreadException();
12358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            targetItemCount -= 2;
12368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // wait until main thread runnables are consumed
12388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        while (targetItemCount != mTestAdapter.getItemCount()) {
12398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(100);
12408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
12428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
12438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
12450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addManyMultiStep() throws Throwable {
12468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10, 1, 7);
12478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
12488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
12498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(1);
12508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // try to trigger race conditions
12518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int targetItemCount = mTestAdapter.getItemCount();
12528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < 100; i++) {
1253e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            checkForMainThreadException();
12548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.addAndNotify(0, 1);
1255e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            checkForMainThreadException();
12568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.addAndNotify(7, 1);
12578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            targetItemCount += 2;
12588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1259e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar        checkForMainThreadException();
12608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // wait until main thread runnables are consumed
12618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        while (targetItemCount != mTestAdapter.getItemCount()) {
12628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(100);
1263e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar            checkForMainThreadException();
12648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
12668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
12678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
12690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void basicDelete() throws Throwable {
12708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10);
12718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final OnLayoutCallbacks callbacks = new OnLayoutCallbacks() {
12728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
12738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void postDispatchLayout() {
12748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // verify this only in first layout
12758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals("deleted views should still be children of RV",
12768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        mLayoutManager.getChildCount() + mDeletedViewCount
12778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        , mRecyclerView.getChildCount());
12788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
12798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
12818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void afterPreLayout(RecyclerView.Recycler recycler,
12828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    AnimationLayoutManager layoutManager,
12838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
12848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.afterPreLayout(recycler, layoutManager, state);
12858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mLayoutItemCount = 3;
12868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mLayoutMin = 0;
12878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
12888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        };
12898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.mLayoutItemCount = 10;
12908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.setExpectedItemCounts(10, 3);
12918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(callbacks);
12928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(2);
12948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mTestAdapter.deleteAndNotify(0, 7);
12958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
12968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.reset();// when animations end another layout will happen
12978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
12988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
13010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterChangeDuringScrolling() throws Throwable {
1302d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        setupBasic(10);
1303d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        final AtomicInteger onLayoutItemCount = new AtomicInteger(0);
1304d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        final AtomicInteger onScrollItemCount = new AtomicInteger(0);
1305d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
1306d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() {
1307d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
1308d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            void onLayoutChildren(RecyclerView.Recycler recycler,
1309d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
1310d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                onLayoutItemCount.set(state.getItemCount());
1311d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                super.onLayoutChildren(recycler, lm, state);
1312d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
1313d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
1314d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
1315d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
1316d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                onScrollItemCount.set(state.getItemCount());
1317d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                super.onScroll(dx, recycler, state);
1318d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
1319d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        });
132042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
1321d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
1322d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            public void run() {
1323d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mTestAdapter.mItems.remove(5);
1324d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mTestAdapter.notifyItemRangeRemoved(5, 1);
1325d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mRecyclerView.scrollBy(0, 100);
1326d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                assertTrue("scrolling while there are pending adapter updates should "
1327d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                        + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0);
1328d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                assertEquals("scroll by should be called w/ updated adapter count",
1329d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                        mTestAdapter.mItems.size(), onScrollItemCount.get());
1330d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
1331d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
1332d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        });
1333d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar    }
1334d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
13350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
13360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void notifyDataSetChangedDuringScroll() throws Throwable {
133717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        setupBasic(10);
133817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        final AtomicInteger onLayoutItemCount = new AtomicInteger(0);
133917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        final AtomicInteger onScrollItemCount = new AtomicInteger(0);
134017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
134117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() {
134217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            @Override
134317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            void onLayoutChildren(RecyclerView.Recycler recycler,
134417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
134517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                onLayoutItemCount.set(state.getItemCount());
134617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                super.onLayoutChildren(recycler, lm, state);
134717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            }
134817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
134917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            @Override
135017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
135117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                onScrollItemCount.set(state.getItemCount());
135217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                super.onScroll(dx, recycler, state);
135317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            }
135417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        });
135542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
135617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            @Override
135717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            public void run() {
135817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                mTestAdapter.mItems.remove(5);
135917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                mTestAdapter.notifyDataSetChanged();
136017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                mRecyclerView.scrollBy(0, 100);
136117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                assertTrue("scrolling while there are pending adapter updates should "
136217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                        + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0);
136317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                assertEquals("scroll by should be called w/ updated adapter count",
136417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                        mTestAdapter.mItems.size(), onScrollItemCount.get());
136517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
136617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            }
136717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        });
136817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar    }
136917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
13700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
13710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addInvisibleAndVisible() throws Throwable {
1372b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
1373b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
1374b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
13752d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{7, 1});// add a new item 0 // invisible
1376b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
1377b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1378b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
13790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
13800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addInvisible() throws Throwable {
1381b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
1382b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(1);
1383b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
13842d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{8, 1});// add a new item 0
1385b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
1386b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1387b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
13880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
13890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void basicAdd() throws Throwable {
1390b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10);
1391b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
1392b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setExpectedItemCounts(10, 13);
1393b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mTestAdapter.addAndNotify(2, 3);
1394b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
1395b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1396b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
13976e51229d1d8dfb7ce1c38c658814ebf8d2ff0706Aurimas Liutikas    // Run this test on Jelly Bean and newer because hasTransientState was introduced in API 16.
13986e51229d1d8dfb7ce1c38c658814ebf8d2ff0706Aurimas Liutikas    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
13990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
14000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void appCancelAnimationInDetach() throws Throwable {
1401a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        final View[] addedView = new View[2];
1402a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        TestAdapter adapter = new TestAdapter(1) {
1403a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            @Override
1404a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            public void onViewDetachedFromWindow(TestViewHolder holder) {
1405a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                if ((addedView[0] == holder.itemView || addedView[1] == holder.itemView)
1406f3e710cfe6ee7027d5ad5b5b55fe00cff4136de8Yigit Boyar                        && ViewCompat.hasTransientState(holder.itemView)) {
1407fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas                    holder.itemView.animate().cancel();
1408a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                }
1409a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                super.onViewDetachedFromWindow(holder);
1410a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            }
1411a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        };
1412a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // original 1 item
1413a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        setupBasic(1, 0, 1, adapter);
1414cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar        mRecyclerView.getItemAnimator().setAddDuration(10000);
1415a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.expectLayouts(2);
1416a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // add 2 items
1417a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        setExpectedItemCounts(1, 3);
1418a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mTestAdapter.addAndNotify(0, 2);
141966826566020afc8d11f183cf3fe443ac0a022384Yigit Boyar        mLayoutManager.waitForLayout(2);
1420629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar        checkForMainThreadException();
1421a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // wait till "add animation" starts
1422e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar        int limit = 200;
1423a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        while (addedView[0] == null || addedView[1] == null) {
1424629687bba8da1a73c48d8fe87393a13581ce46caYigit Boyar            Thread.sleep(100);
142542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas            mActivityRule.runOnUiThread(new Runnable() {
1426a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                @Override
1427a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                public void run() {
1428a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    if (mRecyclerView.getChildCount() == 3) {
1429a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        View view = mRecyclerView.getChildAt(0);
1430f3e710cfe6ee7027d5ad5b5b55fe00cff4136de8Yigit Boyar                        if (ViewCompat.hasTransientState(view)) {
1431a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                            addedView[0] = view;
1432a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        }
1433a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        view = mRecyclerView.getChildAt(1);
1434f3e710cfe6ee7027d5ad5b5b55fe00cff4136de8Yigit Boyar                        if (ViewCompat.hasTransientState(view)) {
1435a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                            addedView[1] = view;
1436a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        }
1437a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    }
1438a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                }
1439a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            });
1440e05fbd9cfe05496e82a3abe19e07e8745985e9a5Yigit Boyar            assertTrue("add should start on time", --limit > 0);
1441a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        }
1442a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu
1443a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // Layout from item2, exclude the current adding items
1444a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.expectLayouts(1);
1445a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
1446a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            @Override
1447a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            void beforePostLayout(RecyclerView.Recycler recycler,
1448a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    AnimationLayoutManager layoutManager,
1449a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    RecyclerView.State state) {
1450a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                mLayoutMin = 2;
1451a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                mLayoutItemCount = 1;
1452a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            }
1453a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        };
1454a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        requestLayoutOnUIThread(mRecyclerView);
1455a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.waitForLayout(2);
1456a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu    }
1457a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu
14580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
14590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void adapterChangeFrozen() throws Throwable {
1460ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        setupBasic(10, 1, 7);
1461ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertTrue(mRecyclerView.getChildCount() == 7);
1462ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1463ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        mLayoutManager.expectLayouts(2);
1464ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
1465ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 8;
1466ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(true);
1467ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        mTestAdapter.addAndNotify(0, 1);
1468ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1469ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        mLayoutManager.assertNoLayout("RV should keep old child during frozen", 2);
1470ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals(7, mRecyclerView.getChildCount());
1471ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
1472ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        freezeLayout(false);
1473ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        mLayoutManager.waitForLayout(2);
1474ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu        assertEquals("RV should get updated after waken from frozen",
1475ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu                8, mRecyclerView.getChildCount());
1476ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu    }
1477ef3e37fe088c7d8e614bbc4ed58d6c0d3fa807a1Dake Gu
14780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
14790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeScrapInvalidate() throws Throwable {
1480888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        setupBasic(10);
1481888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        TestRecyclerView testRecyclerView = getTestRecyclerView();
1482888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        mLayoutManager.expectLayouts(1);
1483888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        testRecyclerView.expectDraw(1);
148442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
1485888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            @Override
1486888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            public void run() {
1487888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                mTestAdapter.mItems.clear();
1488888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                mTestAdapter.notifyDataSetChanged();
1489888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            }
1490888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        });
1491888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        mLayoutManager.waitForLayout(2);
1492888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        testRecyclerView.waitForDraw(2);
1493888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    }
1494888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
14950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
14960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void deleteVisibleAndInvisible() throws Throwable {
1497b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(11, 3, 5); //layout items  3 4 5 6 7
1498b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
14998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setLayoutRange(3, 5); //layout previously invisible child 10 from end of the list
1500b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setExpectedItemCounts(9, 8);
1501b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mTestAdapter.deleteAndNotify(new int[]{4, 1}, new int[]{7, 2});// delete items 4, 8, 9
1502b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
1503b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1504b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
15050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
15060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void findPositionOffset() throws Throwable {
15078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10);
150890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
150990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            @Override
151090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            void beforePreLayout(RecyclerView.Recycler recycler,
151190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
151290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                super.beforePreLayout(recycler, lm, state);
151390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                // [0,2,4]
151490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertEquals("offset check", 0, mAdapterHelper.findPositionOffset(0));
151590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertEquals("offset check", 1, mAdapterHelper.findPositionOffset(2));
151690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertEquals("offset check", 2, mAdapterHelper.findPositionOffset(4));
151790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            }
151890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        };
151942e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
15208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
15218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void run() {
1522121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                try {
1523121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    // delete 1
1524121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    mTestAdapter.deleteAndNotify(1, 1);
1525121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    // delete 3
1526121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    mTestAdapter.deleteAndNotify(2, 1);
1527121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                } catch (Throwable throwable) {
1528121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    throwable.printStackTrace();
1529121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                }
15308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
15318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        });
153290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        mLayoutManager.waitForLayout(2);
15338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
15348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1535b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private void setLayoutRange(int start, int count) {
1536b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = start;
1537b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = count;
1538b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1539b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1540b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private void setExpectedItemCounts(int preLayout, int postLayout) {
1541b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(preLayout, postLayout);
1542b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1543b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
15440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
15450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void deleteInvisible() throws Throwable {
1546b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
15478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
15488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
1549b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(1);
1550b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(8, 8);
15518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});// delete item id 0,8
1552b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
1553b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1554b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1555e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    private CollectPositionResult findByPos(RecyclerView recyclerView,
1556e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            RecyclerView.Recycler recycler, RecyclerView.State state, int position) {
1557e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        View view = recycler.getViewForPosition(position, true);
1558e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view);
1559e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        if (vh.wasReturnedFromScrap()) {
1560e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            vh.clearReturnedFromScrapFlag(); //keep data consistent.
1561e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            return CollectPositionResult.fromScrap(vh);
1562e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        } else {
1563e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            return CollectPositionResult.fromAdapter(vh);
15648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
15658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
15668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1567e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public Map<Integer, CollectPositionResult> collectPositions(RecyclerView recyclerView,
1568e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            RecyclerView.Recycler recycler, RecyclerView.State state, int... positions) {
15698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        Map<Integer, CollectPositionResult> positionToAdapterMapping
15708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                = new HashMap<Integer, CollectPositionResult>();
15718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int position : positions) {
15728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (position < 0) {
15738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                continue;
15748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
1575e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            positionToAdapterMapping.put(position,
1576e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    findByPos(recyclerView, recycler, state, position));
15778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
15788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return positionToAdapterMapping;
15798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
15808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
15810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
15820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addDelete2() throws Throwable {
15838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(5, 0, 5, new AdapterOps() {
15849051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 1 2 3 4
15859051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 1 2 a b 3 4
15869051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 1 b 3 4
15879051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // pre: 0 1 2 3 4
15889051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // pre w/ adap: 0 1 2 b 3 4
15899051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
15909051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void onRun(TestAdapter adapter) throws Throwable {
15919051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{2, -2});
15929051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
15939051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                }, PositionConstraint.scrap(2, 2, -1), PositionConstraint.scrap(1, 1, 1),
15948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(3, 3, 3)
15958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
15968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
1597d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
15980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
15990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addDelete1() throws Throwable {
16008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(5, 0, 5, new AdapterOps() {
16019051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 1 2 3 4
16029051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 1 2 a b 3 4
16039051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 2 a b 3 4
16049051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 c d 2 a b 3 4
16059051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // 0 c d 2 a 4
16069051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // c d 2 a 4
16079051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    // pre: 0 1 2 3 4
16089051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
16099051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void onRun(TestAdapter adapter) throws Throwable {
16109051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{1, -1},
16119051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                                new int[]{1, 2}, new int[]{5, -2}, new int[]{0, -1});
16129051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
16139051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                }, PositionConstraint.scrap(0, 0, -1), PositionConstraint.scrap(1, 1, -1),
16148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(2, 2, 2), PositionConstraint.scrap(3, 3, -1),
16158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 4, 4), PositionConstraint.adapter(0),
16168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.adapter(1), PositionConstraint.adapter(3)
16178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
16188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
16198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
16200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
16210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addSameIndexTwice() throws Throwable {
16228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(12, 2, 7, new AdapterOps() {
16239051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
16249051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void onRun(TestAdapter adapter) throws Throwable {
16259051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        adapter.addAndNotify(new int[]{1, 2}, new int[]{5, 1}, new int[]{5, 1},
16269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                                new int[]{11, 1});
16279051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
16289051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                }, PositionConstraint.adapterScrap(0, 0), PositionConstraint.adapterScrap(1, 3),
16298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(2, 2, 4), PositionConstraint.scrap(3, 3, 7),
16308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 4, 8), PositionConstraint.scrap(7, 7, 12),
16318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(8, 8, 13)
16328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
16338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
16348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
16350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
16360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void deleteTwice() throws Throwable {
16378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(12, 2, 7, new AdapterOps() {
16389051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    @Override
16399051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    void onRun(TestAdapter adapter) throws Throwable {
16409051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                        adapter.deleteAndNotify(new int[]{0, 1}, new int[]{1, 1}, new int[]{7, 1},
16419051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                                new int[]{0, 1});// delete item ids 0,2,9,1
16429051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    }
16439051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                }, PositionConstraint.scrap(2, 0, -1), PositionConstraint.scrap(3, 1, 0),
16448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 2, 1), PositionConstraint.scrap(5, 3, 2),
16458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(6, 4, 3), PositionConstraint.scrap(8, 6, 5),
16468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.adapterScrap(7, 6), PositionConstraint.adapterScrap(8, 7)
16478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
16488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
16498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1650b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
16518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void positionStatesTest(int itemCount, int firstLayoutStartIndex,
1652e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            int firstLayoutItemCount, AdapterOps adapterChanges,
1653e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            final PositionConstraint... constraints) throws Throwable {
1654e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        positionStatesTest(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null,
16559051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                adapterChanges, constraints);
1656e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
16579051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
1658e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public void positionStatesTest(int itemCount, int firstLayoutStartIndex,
16599051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            int firstLayoutItemCount, TestAdapter adapter, AdapterOps adapterChanges,
1660e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            final PositionConstraint... constraints) throws Throwable {
1661e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, adapter);
1662b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
16638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
16648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
16658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void beforePreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
16668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
16678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.beforePreLayout(recycler, lm, state);
16688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                //harmless
16698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                lm.detachAndScrapAttachedViews(recycler);
16708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                final int[] ids = new int[constraints.length];
16718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int i = 0; i < constraints.length; i++) {
16728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ids[i] = constraints[i].mPreLayoutPos;
16738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
16748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                Map<Integer, CollectPositionResult> positions
1675e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        = collectPositions(lm.mRecyclerView, recycler, state, ids);
1676121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                StringBuilder positionLog = new StringBuilder("\nPosition logs:\n");
1677121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                for (Map.Entry<Integer, CollectPositionResult> entry : positions.entrySet()) {
16789051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                    positionLog.append(entry.getKey()).append(":").append(entry.getValue())
16799051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                            .append("\n");
1680121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                }
16818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (PositionConstraint constraint : constraints) {
16828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (constraint.mPreLayoutPos != -1) {
16838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        constraint.validate(state, positions.get(constraint.mPreLayoutPos),
1684121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                                lm.getLog() + positionLog);
16858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
16868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
16878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
16888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
16898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
16908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
16918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
16928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.beforePostLayout(recycler, lm, state);
16938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                lm.detachAndScrapAttachedViews(recycler);
16948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                final int[] ids = new int[constraints.length];
16958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int i = 0; i < constraints.length; i++) {
16968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ids[i] = constraints[i].mPostLayoutPos;
16978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
16988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                Map<Integer, CollectPositionResult> positions
1699e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        = collectPositions(lm.mRecyclerView, recycler, state, ids);
1700121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                StringBuilder positionLog = new StringBuilder("\nPosition logs:\n");
1701121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                for (Map.Entry<Integer, CollectPositionResult> entry : positions.entrySet()) {
1702121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                    positionLog.append(entry.getKey()).append(":")
1703121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                            .append(entry.getValue()).append("\n");
1704121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                }
17058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (PositionConstraint constraint : constraints) {
17068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (constraint.mPostLayoutPos >= 0) {
17078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        constraint.validate(state, positions.get(constraint.mPostLayoutPos),
1708121ba9616e5bed44d2490f1744f7b6a9d3e79866Yigit Boyar                                lm.getLog() + positionLog);
17098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
17108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
17118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
17128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        };
17138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        adapterChanges.run(mTestAdapter);
1714b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
1715e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        checkForMainThreadException();
17168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (PositionConstraint constraint : constraints) {
17178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.assertValidate();
17188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
1719b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1720b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
17210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
17220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void addThenRecycleRemovedView() throws Throwable {
17236a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        setupBasic(10);
17246a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        final AtomicInteger step = new AtomicInteger(0);
17259051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        final List<RecyclerView.ViewHolder> animateRemoveList
17269051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
17276a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        DefaultItemAnimator animator = new DefaultItemAnimator() {
17286a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            @Override
17296a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            public boolean animateRemove(RecyclerView.ViewHolder holder) {
17306a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                animateRemoveList.add(holder);
17316a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                return super.animateRemove(holder);
17326a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            }
17336a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        };
17346a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        mRecyclerView.setItemAnimator(animator);
17356a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        final List<RecyclerView.ViewHolder> pooledViews = new ArrayList<RecyclerView.ViewHolder>();
17366a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() {
17376a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            @Override
17386a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
17396a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                pooledViews.add(scrap);
17406a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                super.putRecycledView(scrap);
17416a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            }
17426a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        });
17436a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        final RecyclerView.ViewHolder[] targetVh = new RecyclerView.ViewHolder[1];
17446a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
17456a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            @Override
17466a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            void doLayout(RecyclerView.Recycler recycler,
17476a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
17486a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                switch (step.get()) {
17496a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                    case 1:
17506a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                        super.doLayout(recycler, lm, state);
17516a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                        if (state.isPreLayout()) {
17526a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                            View view = mLayoutManager.getChildAt(1);
17536a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                            RecyclerView.ViewHolder holder =
17546a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                                    mRecyclerView.getChildViewHolderInt(view);
17556a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                            targetVh[0] = holder;
17566a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                            assertTrue("test sanity", holder.isRemoved());
17576a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                            mLayoutManager.removeAndRecycleView(view, recycler);
17586a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                        }
17596a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                        break;
17606a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                }
17616a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar            }
17626a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        };
17636a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        step.set(1);
17646a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        animateRemoveList.clear();
17656a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        mLayoutManager.expectLayouts(2);
17666a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        mTestAdapter.deleteAndNotify(1, 1);
17676a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        mLayoutManager.waitForLayout(2);
17686a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        assertTrue("test sanity, view should be recycled", pooledViews.contains(targetVh[0]));
17696a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar        assertTrue("since LM force recycled a view, animate disappearance should not be called",
17706a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar                animateRemoveList.isEmpty());
17716a6748f8ec1d1f2824cf5c7c6ee8ade8cad0a63aYigit Boyar    }
1772d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase}
1773