1d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar/*
2d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
3d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *
4d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * you may not use this file except in compliance with the License.
6d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * You may obtain a copy of the License at
7d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *
8d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar *
10d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * See the License for the specific language governing permissions and
14d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar * limitations under the License.
15d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar */
16d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
17d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpackage android.support.v7.widget;
18d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
19e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
20e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
21e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas
22e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertEquals;
23e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertNotNull;
24e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertSame;
25e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport static org.junit.Assert.assertTrue;
260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
27a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat;
28a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityEventCompat;
29a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityRecordCompat;
30f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyarimport android.test.suitebuilder.annotation.MediumTest;
318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log;
32d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View;
33504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup;
34a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent;
35d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
36e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikasimport org.junit.Test;
37e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas
38d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList;
39d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List;
40504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
41d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/**
438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}.
448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p>
458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation
468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling
478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing.
488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */
49f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar@MediumTest
50999c3976674d20b0de5425490bdfe7415b9c6af2Yigit Boyarpublic class LinearLayoutManagerTest extends BaseLinearLayoutManagerTest {
518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar
520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItem() throws Throwable {
541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(
561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItemReverse() throws Throwable {
611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(true), 100,
631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                0);
641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItemStackFromEnd() throws Throwable {
681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(false), 100,
701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItemStackFromEndAndReverse() throws Throwable {
751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(true), 100,
771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItemHorizontal() throws Throwable {
821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(
841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        false), 100, 0);
851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItemReverseHorizontal() throws Throwable {
891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(true),
911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 0);
921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItemStackFromEndHorizontal() throws Throwable {
961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(false),
981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                100, 99);
991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void removeAnchorItemStackFromEndAndReverseHorizontal() throws Throwable {
1031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        removeAnchorItemTest(
1041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(true), 100,
1051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                99);
1061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    /**
1091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * This tests a regression where predictive animations were not working as expected when the
1101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * first item is removed and there aren't any more items to add from that direction.
1111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     * First item refers to the default anchor item.
1121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar     */
1131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    public void removeAnchorItemTest(final Config config, int adapterSize,
1141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            final int removePos) throws Throwable {
1151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        config.adapter(new TestAdapter(adapterSize) {
1161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            @Override
1171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
1181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    int position) {
1191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                super.onBindViewHolder(holder, position);
1201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
1211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (!(lp instanceof ViewGroup.MarginLayoutParams)) {
1221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    lp = new ViewGroup.MarginLayoutParams(0, 0);
1231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    holder.itemView.setLayoutParams(lp);
1241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
1261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int maxSize;
1271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
1281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getWidth();
129e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    mlp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
1301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    maxSize = mRecyclerView.getHeight();
132e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    mlp.width = ViewGroup.MarginLayoutParams.MATCH_PARENT;
1331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                final int desiredSize;
1361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (position == removePos) {
1371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it large
1381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 4;
1391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    // make it small
1411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    desiredSize = maxSize / 8;
1421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
1441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.width = desiredSize;
1451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                } else {
1461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    mlp.height = desiredSize;
1471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                }
1481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
1491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        });
1501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        setupByConfig(config, true);
1511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
1521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        RecyclerView.ViewHolder toBeRemoved = null;
1531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>();
1541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < childCount; i++) {
1551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mLayoutManager.getChildAt(i);
1561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
1571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (holder.getAdapterPosition() == removePos) {
1581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeRemoved = holder;
1591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            } else {
1601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                toBeMoved.add(holder);
1611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
1621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
1631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertNotNull("test sanity", toBeRemoved);
1641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("test sanity", childCount - 1, toBeMoved.size());
1651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator();
1661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mRecyclerView.setItemAnimator(loggingItemAnimator);
1671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.reset();
1681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.expectRunPendingAnimationsCall(1);
1691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mLayoutManager.expectLayouts(2);
1701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mTestAdapter.deleteAndNotify(removePos, 1);
1711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        mLayoutManager.waitForLayout(1);
1721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        loggingItemAnimator.waitForPendingAnimationsCall(2);
1731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("removed child should receive remove animation",
1741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mRemoveVHs.contains(toBeRemoved));
1751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder vh : toBeMoved) {
1761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("view holder should be in moved list",
1771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(vh));
1781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
1791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>();
1801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
1811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            View child = mLayoutManager.getChildAt(i);
1821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child);
1831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            if (toBeRemoved != holder && !toBeMoved.contains(holder)) {
1841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                newHolders.add(holder);
1851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            }
1861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
1871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("some new children should show up for the new space", newHolders.size() > 0);
1881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertEquals("no items should receive animate add since they are not new", 0,
1891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                loggingItemAnimator.mAddVHs.size());
1901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        for (RecyclerView.ViewHolder holder : newHolders) {
1911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar            assertTrue("new holder should receive a move animation",
1921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                    loggingItemAnimator.mMoveVHs.contains(holder));
1931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        }
1941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar        assertTrue("control against adding too many children due to bad layout state preparation."
1951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                        + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(),
1961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar                mRecyclerView.getChildCount() <= childCount + 3 /*1 for removed view, 2 for its size*/);
1971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar    }
1981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar
1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void keepFocusOnRelayout() throws Throwable {
201310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true);
202310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        int center = (mLayoutManager.findLastVisibleItemPosition()
203310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                - mLayoutManager.findFirstVisibleItemPosition()) / 2;
204115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(center);
205310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView);
206959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        requestFocus(vh.itemView, true);
207310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertTrue("view should have the focus", vh.itemView.hasFocus());
208310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        // add a bunch of items right before that view, make sure it keeps its position
209310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.expectLayouts(2);
210310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        final int childCountToAdd = mRecyclerView.getChildCount() * 2;
211310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mTestAdapter.addAndNotify(center, childCountToAdd);
212310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        center += childCountToAdd; // offset item
213310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForLayout(2);
214310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        mLayoutManager.waitForAnimationsToEnd(20);
215115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar        final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForLayoutPosition(center);
216310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertNotNull("focused child should stay in layout", postVH);
217310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertSame("same view holder should be kept for unchanged child", vh, postVH);
218310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        assertEquals("focused child's screen position should stay unchanged", top,
219310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar                mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView));
220310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar    }
221310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar
2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void keepFullFocusOnResize() throws Throwable {
224542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), true);
225542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
226542f1260934df280985294eaef1ec8469863281fYigit Boyar
2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void keepPartialFocusOnResize() throws Throwable {
229542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), false);
230542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
231542f1260934df280985294eaef1ec8469863281fYigit Boyar
2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void keepReverseFullFocusOnResize() throws Throwable {
234542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), true);
235542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
236542f1260934df280985294eaef1ec8469863281fYigit Boyar
2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void keepReversePartialFocusOnResize() throws Throwable {
239542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), false);
240542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
241542f1260934df280985294eaef1ec8469863281fYigit Boyar
2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void keepStackFromEndFullFocusOnResize() throws Throwable {
244542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), true);
245542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
246542f1260934df280985294eaef1ec8469863281fYigit Boyar
2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void keepStackFromEndPartialFocusOnResize() throws Throwable {
249542f1260934df280985294eaef1ec8469863281fYigit Boyar        keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), false);
250542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
251542f1260934df280985294eaef1ec8469863281fYigit Boyar
252542f1260934df280985294eaef1ec8469863281fYigit Boyar    public void keepFocusOnResizeTest(final Config config, boolean fullyVisible) throws Throwable {
253542f1260934df280985294eaef1ec8469863281fYigit Boyar        setupByConfig(config, true);
254542f1260934df280985294eaef1ec8469863281fYigit Boyar        final int targetPosition;
255542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (config.mStackFromEnd) {
256542f1260934df280985294eaef1ec8469863281fYigit Boyar            targetPosition = mLayoutManager.findFirstVisibleItemPosition();
257542f1260934df280985294eaef1ec8469863281fYigit Boyar        } else {
258542f1260934df280985294eaef1ec8469863281fYigit Boyar            targetPosition = mLayoutManager.findLastVisibleItemPosition();
259542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
260542f1260934df280985294eaef1ec8469863281fYigit Boyar        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
261542f1260934df280985294eaef1ec8469863281fYigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView
262542f1260934df280985294eaef1ec8469863281fYigit Boyar                .findViewHolderForLayoutPosition(targetPosition);
263542f1260934df280985294eaef1ec8469863281fYigit Boyar
264542f1260934df280985294eaef1ec8469863281fYigit Boyar        // scroll enough to offset the child
265542f1260934df280985294eaef1ec8469863281fYigit Boyar        int startMargin = helper.getDecoratedStart(vh.itemView) -
266542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getStartAfterPadding();
267542f1260934df280985294eaef1ec8469863281fYigit Boyar        int endMargin = helper.getEndAfterPadding() -
268542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedEnd(vh.itemView);
269542f1260934df280985294eaef1ec8469863281fYigit Boyar        Log.d(TAG, "initial start margin " + startMargin + " , end margin:" + endMargin);
270959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        requestFocus(vh.itemView, true);
271959f4c0fac89425a8a9842e82fc180ec736fffacYigit Boyar        assertTrue("view should gain the focus", vh.itemView.hasFocus());
272542f1260934df280985294eaef1ec8469863281fYigit Boyar        // scroll enough to offset the child
273542f1260934df280985294eaef1ec8469863281fYigit Boyar        startMargin = helper.getDecoratedStart(vh.itemView) -
274542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getStartAfterPadding();
275542f1260934df280985294eaef1ec8469863281fYigit Boyar        endMargin = helper.getEndAfterPadding() -
276542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedEnd(vh.itemView);
277542f1260934df280985294eaef1ec8469863281fYigit Boyar
278542f1260934df280985294eaef1ec8469863281fYigit Boyar        Log.d(TAG, "start margin " + startMargin + " , end margin:" + endMargin);
279542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("View should become fully visible", startMargin >= 0 && endMargin >= 0);
280542f1260934df280985294eaef1ec8469863281fYigit Boyar
281542f1260934df280985294eaef1ec8469863281fYigit Boyar        int expectedOffset = 0;
282542f1260934df280985294eaef1ec8469863281fYigit Boyar        boolean offsetAtStart = false;
283542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (!fullyVisible) {
284542f1260934df280985294eaef1ec8469863281fYigit Boyar            // move it a bit such that it is no more fully visible
285542f1260934df280985294eaef1ec8469863281fYigit Boyar            final int childSize = helper
286542f1260934df280985294eaef1ec8469863281fYigit Boyar                    .getDecoratedMeasurement(vh.itemView);
287542f1260934df280985294eaef1ec8469863281fYigit Boyar            expectedOffset = childSize / 3;
288542f1260934df280985294eaef1ec8469863281fYigit Boyar            if (startMargin < endMargin) {
289542f1260934df280985294eaef1ec8469863281fYigit Boyar                scrollBy(expectedOffset);
290542f1260934df280985294eaef1ec8469863281fYigit Boyar                offsetAtStart = true;
291542f1260934df280985294eaef1ec8469863281fYigit Boyar            } else {
292542f1260934df280985294eaef1ec8469863281fYigit Boyar                scrollBy(-expectedOffset);
293542f1260934df280985294eaef1ec8469863281fYigit Boyar                offsetAtStart = false;
294542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
295542f1260934df280985294eaef1ec8469863281fYigit Boyar            startMargin = helper.getDecoratedStart(vh.itemView) -
296542f1260934df280985294eaef1ec8469863281fYigit Boyar                    helper.getStartAfterPadding();
297542f1260934df280985294eaef1ec8469863281fYigit Boyar            endMargin = helper.getEndAfterPadding() -
298542f1260934df280985294eaef1ec8469863281fYigit Boyar                    helper.getDecoratedEnd(vh.itemView);
299542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("test sanity, view should not be fully visible", startMargin < 0
300542f1260934df280985294eaef1ec8469863281fYigit Boyar                    || endMargin < 0);
301542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
302542f1260934df280985294eaef1ec8469863281fYigit Boyar
303542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.expectLayouts(1);
304542f1260934df280985294eaef1ec8469863281fYigit Boyar        runTestOnUiThread(new Runnable() {
305542f1260934df280985294eaef1ec8469863281fYigit Boyar            @Override
306542f1260934df280985294eaef1ec8469863281fYigit Boyar            public void run() {
307542f1260934df280985294eaef1ec8469863281fYigit Boyar                final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams();
308542f1260934df280985294eaef1ec8469863281fYigit Boyar                if (config.mOrientation == HORIZONTAL) {
309542f1260934df280985294eaef1ec8469863281fYigit Boyar                    layoutParams.width = mRecyclerView.getWidth() / 2;
310542f1260934df280985294eaef1ec8469863281fYigit Boyar                } else {
311542f1260934df280985294eaef1ec8469863281fYigit Boyar                    layoutParams.height = mRecyclerView.getHeight() / 2;
312542f1260934df280985294eaef1ec8469863281fYigit Boyar                }
313542f1260934df280985294eaef1ec8469863281fYigit Boyar                mRecyclerView.setLayoutParams(layoutParams);
314542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
315542f1260934df280985294eaef1ec8469863281fYigit Boyar        });
316542f1260934df280985294eaef1ec8469863281fYigit Boyar        Thread.sleep(100);
317542f1260934df280985294eaef1ec8469863281fYigit Boyar        // add a bunch of items right before that view, make sure it keeps its position
318542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.waitForLayout(2);
319542f1260934df280985294eaef1ec8469863281fYigit Boyar        mLayoutManager.waitForAnimationsToEnd(20);
320542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("view should preserve the focus", vh.itemView.hasFocus());
321542f1260934df280985294eaef1ec8469863281fYigit Boyar        final RecyclerView.ViewHolder postVH = mRecyclerView
322542f1260934df280985294eaef1ec8469863281fYigit Boyar                .findViewHolderForLayoutPosition(targetPosition);
323542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertNotNull("focused child should stay in layout", postVH);
324542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertSame("same view holder should be kept for unchanged child", vh, postVH);
325542f1260934df280985294eaef1ec8469863281fYigit Boyar        View focused = postVH.itemView;
326542f1260934df280985294eaef1ec8469863281fYigit Boyar
327542f1260934df280985294eaef1ec8469863281fYigit Boyar        startMargin = helper.getDecoratedStart(focused) - helper.getStartAfterPadding();
328542f1260934df280985294eaef1ec8469863281fYigit Boyar        endMargin = helper.getEndAfterPadding() - helper.getDecoratedEnd(focused);
329542f1260934df280985294eaef1ec8469863281fYigit Boyar
330542f1260934df280985294eaef1ec8469863281fYigit Boyar        assertTrue("focused child should be somewhat visible",
331542f1260934df280985294eaef1ec8469863281fYigit Boyar                helper.getDecoratedStart(focused) < helper.getEndAfterPadding()
332542f1260934df280985294eaef1ec8469863281fYigit Boyar                        && helper.getDecoratedEnd(focused) > helper.getStartAfterPadding());
333542f1260934df280985294eaef1ec8469863281fYigit Boyar        if (fullyVisible) {
334542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("focused child end should stay fully visible",
335542f1260934df280985294eaef1ec8469863281fYigit Boyar                    endMargin >= 0);
336542f1260934df280985294eaef1ec8469863281fYigit Boyar            assertTrue("focused child start should stay fully visible",
337542f1260934df280985294eaef1ec8469863281fYigit Boyar                    startMargin >= 0);
338542f1260934df280985294eaef1ec8469863281fYigit Boyar        } else {
339542f1260934df280985294eaef1ec8469863281fYigit Boyar            if (offsetAtStart) {
340542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("start should preserve its offset", startMargin < 0);
341542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("end should be visible", endMargin >= 0);
342542f1260934df280985294eaef1ec8469863281fYigit Boyar            } else {
343542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("end should preserve its offset", endMargin < 0);
344542f1260934df280985294eaef1ec8469863281fYigit Boyar                assertTrue("start should be visible", startMargin >= 0);
345542f1260934df280985294eaef1ec8469863281fYigit Boyar            }
346542f1260934df280985294eaef1ec8469863281fYigit Boyar        }
347542f1260934df280985294eaef1ec8469863281fYigit Boyar    }
348542f1260934df280985294eaef1ec8469863281fYigit Boyar
3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollToPositionWithPredictive() throws Throwable {
3516e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET);
3526e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
3536e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(3, 20);
3546e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
3556e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2,
3566e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar                LinearLayoutManager.INVALID_OFFSET);
3576e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        removeRecyclerView();
3586e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar        scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10);
3596e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar    }
3606e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar
3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleDuringAnimations() throws Throwable {
363504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
364504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(300) {
365504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
366504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
367504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
368504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final int cnt = childCount.incrementAndGet();
369504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
370504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
371504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder);
372504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
373504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return testViewHolder;
374504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
375504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
376310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar        setupByConfig(new Config(VERTICAL, false, false).itemCount(300)
377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                .adapter(adapter), true);
378504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
379504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() {
380504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
381504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
382504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
383504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                int cnt = childCount.decrementAndGet();
384504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (DEBUG) {
385504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap);
386504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
387504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
388504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
389504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
390504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
391504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
392504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
393504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    final int cnt = childCount.incrementAndGet();
394504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    if (DEBUG) {
395504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                        Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView);
396504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    }
397504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
398504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
399504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
400504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
401504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500);
402504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(pool);
403504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
404504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
405504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
406504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
407504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
408504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(15);
409504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
410504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
411504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
412504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
413504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
414504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
415504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now trigger lots of add again, followed by a scroll to position
416504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
417504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(5 + (i % 3) * 3, 1);
418504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(25);
419504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
420504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20);
421504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
422504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
423504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
424504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
425504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
426504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
427d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar
4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dontRecycleChildrenOnDetach() throws Throwable {
43049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(false), true);
43149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
43249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
43349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
43449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
43534a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                ((ViewGroup)mRecyclerView.getParent()).removeView(mRecyclerView);
43649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("No views are recycled", recyclerSize,
43749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
43849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
43949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
44049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
44149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
4420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void recycleChildrenOnDetach() throws Throwable {
44449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        setupByConfig(new Config().recycleChildrenOnDetach(true), true);
44549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        final int childCount = mLayoutManager.getChildCount();
44649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        runTestOnUiThread(new Runnable() {
44749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            @Override
44849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            public void run() {
44949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
45049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews(
45149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mTestAdapter.getItemViewType(0), recyclerSize + childCount);
45234a33c72e21528b39fe39ca1048ebb9c0d617647Yigit Boyar                ((ViewGroup)mRecyclerView.getParent()).removeView(mRecyclerView);
45349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                assertEquals("All children should be recycled", childCount + recyclerSize,
45449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar                        mRecyclerView.mRecycler.getRecycledViewPool().size());
45549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar            }
45649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar        });
45749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar    }
45849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar
4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollAndClear() throws Throwable {
4613d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        setupByConfig(new Config(), true);
4623d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
4633d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        assertTrue("Children not laid out", mLayoutManager.collectChildCoordinates().size() > 0);
4643d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
4653d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        mLayoutManager.expectLayouts(1);
4663d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        runTestOnUiThread(new Runnable() {
4673d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            @Override
4683d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            public void run() {
4693d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                mLayoutManager.scrollToPositionWithOffset(1, 0);
4703d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev                mTestAdapter.clearOnUIThread();
4713d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev            }
4723d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        });
4733d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        mLayoutManager.waitForLayout(2);
4743d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
4753d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev        assertEquals("Remaining children", 0, mLayoutManager.collectChildCoordinates().size());
4763d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev    }
4773d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
4783d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev
4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void accessibilityPositions() throws Throwable {
481a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        setupByConfig(new Config(VERTICAL, false, false), true);
482a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityDelegateCompat delegateCompat = mRecyclerView
483a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .getCompatAccessibilityDelegate();
484a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityEvent event = AccessibilityEvent.obtain();
485a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        runTestOnUiThread(new Runnable() {
486a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            @Override
487a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            public void run() {
488a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event);
489a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar            }
490a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        });
491a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        final AccessibilityRecordCompat record = AccessibilityEventCompat
492a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                .asRecord(event);
493a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have first position",
494a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                record.getFromIndex(),
495a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                mLayoutManager.findFirstVisibleItemPosition());
496a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar        assertEquals("result should have last position",
497a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                record.getToIndex(),
498a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar                mLayoutManager.findLastVisibleItemPosition());
499a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar    }
500d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar}
501