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 19d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.content.Context; 208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.graphics.Rect; 218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcel; 228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.os.Parcelable; 23a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.AccessibilityDelegateCompat; 24a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityEventCompat; 25a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport android.util.Log; 27d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport android.view.View; 28504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup; 29a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyarimport android.view.accessibility.AccessibilityEvent; 30310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport android.widget.FrameLayout; 31d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 32c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_END; 33c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START; 34310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; 35310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL; 368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.lang.reflect.Field; 37d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.ArrayList; 388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.LinkedHashMap; 39d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.List; 408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.Map; 418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyarimport java.util.UUID; 42d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.CountDownLatch; 43d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarimport java.util.concurrent.TimeUnit; 44504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport java.util.concurrent.atomic.AtomicInteger; 45d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar/** 478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Includes tests for {@link LinearLayoutManager}. 488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * <p> 498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * Since most UI tests are not practical, these tests are focused on internal data representation 508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * and stability of LinearLayoutManager in response to different events (state change, scrolling 518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar * etc) where it is very hard to do manual testing. 528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar */ 53d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyarpublic class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest { 54d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final boolean DEBUG = false; 568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private static final String TAG = "LinearLayoutManagerTest"; 588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 59d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar WrappedLinearLayoutManager mLayoutManager; 60d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 61d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar TestAdapter mTestAdapter; 62d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 63d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final List<Config> mBaseVariations = new ArrayList<Config>(); 64d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 65d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 66d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar protected void setUp() throws Exception { 67d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.setUp(); 68310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar for (int orientation : new int[]{VERTICAL, HORIZONTAL}) { 69d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean reverseLayout : new boolean[]{false, true}) { 70d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (boolean stackFromBottom : new boolean[]{false, true}) { 71d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom)); 72d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 73d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 74d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 75d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 76d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar protected List<Config> addConfigVariation(List<Config> base, String fieldName, 788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Object... variations) 798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException { 808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar List<Config> newConfigs = new ArrayList<Config>(); 818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Field field = Config.class.getDeclaredField(fieldName); 828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Config config : base) { 838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Object variation : variations) { 848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Config newConfig = (Config) config.clone(); 858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar field.set(newConfig, variation); 868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar newConfigs.add(newConfig); 878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return newConfigs; 908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable { 93e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView = inflateWrappedRV(); 94542f1260934df280985294eaef1ec8469863281fYigit Boyar 958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setHasFixedSize(true); 96504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount) 97504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar : config.mTestAdapter; 988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setAdapter(mTestAdapter); 99d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation, 100d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config.mReverseLayout); 101d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 10249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 1038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mRecyclerView.setLayoutManager(mLayoutManager); 1048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForFirstLayout) { 1058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 1068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItem() throws Throwable { 1101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout( 1121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar false), 100, 0); 1131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemReverse() throws Throwable { 1161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(false).reverseLayout(true), 100, 1181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 0); 1191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEnd() throws Throwable { 1221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(false), 100, 1241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 1251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEndAndReverse() throws Throwable { 1281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(VERTICAL).stackFromBottom(true).reverseLayout(true), 100, 1301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 1311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemHorizontal() throws Throwable { 1341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout( 1361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar false), 100, 0); 1371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemReverseHorizontal() throws Throwable { 1401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(false).reverseLayout(true), 1421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 100, 0); 1431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEndHorizontal() throws Throwable { 1461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(false), 1481f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 100, 99); 1491f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1501f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1511f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void testRemoveAnchorItemStackFromEndAndReverseHorizontal() throws Throwable { 1521f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar removeAnchorItemTest( 1531f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar new Config().orientation(HORIZONTAL).stackFromBottom(true).reverseLayout(true), 100, 1541f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 99); 1551f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1561f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1571f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar /** 1581f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * This tests a regression where predictive animations were not working as expected when the 1591f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * first item is removed and there aren't any more items to add from that direction. 1601f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar * First item refers to the default anchor item. 1611f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar */ 1621f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void removeAnchorItemTest(final Config config, int adapterSize, 1631f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int removePos) throws Throwable { 1641f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar config.adapter(new TestAdapter(adapterSize) { 1651f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar @Override 1661f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 1671f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar int position) { 1681f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar super.onBindViewHolder(holder, position); 1691f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); 1701f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (!(lp instanceof ViewGroup.MarginLayoutParams)) { 1711f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar lp = new ViewGroup.MarginLayoutParams(0, 0); 1721f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar holder.itemView.setLayoutParams(lp); 1731f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1741f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp; 1751f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int maxSize; 1761f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1771f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar maxSize = mRecyclerView.getWidth(); 1781f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.height = ViewGroup.MarginLayoutParams.FILL_PARENT; 1791f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1801f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar maxSize = mRecyclerView.getHeight(); 1811f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.width = ViewGroup.MarginLayoutParams.FILL_PARENT; 1821f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1831f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 1841f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int desiredSize; 1851f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (position == removePos) { 1861f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // make it large 1871f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar desiredSize = maxSize / 4; 1881f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1891f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar // make it small 1901f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar desiredSize = maxSize / 8; 1911f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1921f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1931f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.width = desiredSize; 1941f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 1951f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mlp.height = desiredSize; 1961f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1971f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 1981f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar }); 1991f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar setupByConfig(config, true); 2001f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 2011f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder toBeRemoved = null; 2021f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar List<RecyclerView.ViewHolder> toBeMoved = new ArrayList<RecyclerView.ViewHolder>(); 2031f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = 0; i < childCount; i++) { 2041f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View child = mLayoutManager.getChildAt(i); 2051f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child); 2061f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (holder.getAdapterPosition() == removePos) { 2071f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar toBeRemoved = holder; 2081f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } else { 2091f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar toBeMoved.add(holder); 2101f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2111f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2121f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertNotNull("test sanity", toBeRemoved); 2131f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertEquals("test sanity", childCount - 1, toBeMoved.size()); 2141f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar LoggingItemAnimator loggingItemAnimator = new LoggingItemAnimator(); 2151f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mRecyclerView.setItemAnimator(loggingItemAnimator); 2161f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.reset(); 2171f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.expectRunPendingAnimationsCall(1); 2181f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutManager.expectLayouts(2); 2191f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mTestAdapter.deleteAndNotify(removePos, 1); 2201f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mLayoutManager.waitForLayout(1); 2211f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.waitForPendingAnimationsCall(2); 2221f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("removed child should receive remove animation", 2231f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mRemoveVHs.contains(toBeRemoved)); 2241f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (RecyclerView.ViewHolder vh : toBeMoved) { 2251f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("view holder should be in moved list", 2261f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mMoveVHs.contains(vh)); 2271f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2281f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar List<RecyclerView.ViewHolder> newHolders = new ArrayList<RecyclerView.ViewHolder>(); 2291f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 2301f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar View child = mLayoutManager.getChildAt(i); 2311f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child); 2321f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar if (toBeRemoved != holder && !toBeMoved.contains(holder)) { 2331f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar newHolders.add(holder); 2341f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2351f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2361f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("some new children should show up for the new space", newHolders.size() > 0); 2371f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertEquals("no items should receive animate add since they are not new", 0, 2381f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mAddVHs.size()); 2391f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar for (RecyclerView.ViewHolder holder : newHolders) { 2401f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("new holder should receive a move animation", 2411f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar loggingItemAnimator.mMoveVHs.contains(holder)); 2421f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2431f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar assertTrue("control against adding too many children due to bad layout state preparation." 2441f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar + " initial:" + childCount + ", current:" + mRecyclerView.getChildCount(), 2451f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar mRecyclerView.getChildCount() <= childCount + 3 /*1 for removed view, 2 for its size*/); 2461f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar } 2471f5c7b76bfc3da85513e6d2c6aa1058339f0c625Yigit Boyar 248310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void testKeepFocusOnRelayout() throws Throwable { 249310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(500), true); 250310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int center = (mLayoutManager.findLastVisibleItemPosition() 251310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar - mLayoutManager.findFirstVisibleItemPosition()) / 2; 252115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(center); 253310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int top = mLayoutManager.mOrientationHelper.getDecoratedStart(vh.itemView); 254310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 255310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 256310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 257310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar vh.itemView.requestFocus(); 258310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 259310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 260310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertTrue("view should have the focus", vh.itemView.hasFocus()); 261310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // add a bunch of items right before that view, make sure it keeps its position 262310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.expectLayouts(2); 263310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final int childCountToAdd = mRecyclerView.getChildCount() * 2; 264310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mTestAdapter.addAndNotify(center, childCountToAdd); 265310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar center += childCountToAdd; // offset item 266310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForLayout(2); 267310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForAnimationsToEnd(20); 268115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final RecyclerView.ViewHolder postVH = mRecyclerView.findViewHolderForLayoutPosition(center); 269310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertNotNull("focused child should stay in layout", postVH); 270310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertSame("same view holder should be kept for unchanged child", vh, postVH); 271310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("focused child's screen position should stay unchanged", top, 272310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.mOrientationHelper.getDecoratedStart(postVH.itemView)); 273310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 274310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 275542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepFullFocusOnResize() throws Throwable { 276542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), true); 277542f1260934df280985294eaef1ec8469863281fYigit Boyar } 278542f1260934df280985294eaef1ec8469863281fYigit Boyar 279542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepPartialFocusOnResize() throws Throwable { 280542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, false).itemCount(500), false); 281542f1260934df280985294eaef1ec8469863281fYigit Boyar } 282542f1260934df280985294eaef1ec8469863281fYigit Boyar 283542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepReverseFullFocusOnResize() throws Throwable { 284542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), true); 285542f1260934df280985294eaef1ec8469863281fYigit Boyar } 286542f1260934df280985294eaef1ec8469863281fYigit Boyar 287542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepReversePartialFocusOnResize() throws Throwable { 288542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, true, false).itemCount(500), false); 289542f1260934df280985294eaef1ec8469863281fYigit Boyar } 290542f1260934df280985294eaef1ec8469863281fYigit Boyar 291542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepStackFromEndFullFocusOnResize() throws Throwable { 292542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), true); 293542f1260934df280985294eaef1ec8469863281fYigit Boyar } 294542f1260934df280985294eaef1ec8469863281fYigit Boyar 295542f1260934df280985294eaef1ec8469863281fYigit Boyar public void testKeepStackFromEndPartialFocusOnResize() throws Throwable { 296542f1260934df280985294eaef1ec8469863281fYigit Boyar keepFocusOnResizeTest(new Config(VERTICAL, false, true).itemCount(500), false); 297542f1260934df280985294eaef1ec8469863281fYigit Boyar } 298542f1260934df280985294eaef1ec8469863281fYigit Boyar 299542f1260934df280985294eaef1ec8469863281fYigit Boyar public void keepFocusOnResizeTest(final Config config, boolean fullyVisible) throws Throwable { 300542f1260934df280985294eaef1ec8469863281fYigit Boyar setupByConfig(config, true); 301542f1260934df280985294eaef1ec8469863281fYigit Boyar final int targetPosition; 302542f1260934df280985294eaef1ec8469863281fYigit Boyar if (config.mStackFromEnd) { 303542f1260934df280985294eaef1ec8469863281fYigit Boyar targetPosition = mLayoutManager.findFirstVisibleItemPosition(); 304542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 305542f1260934df280985294eaef1ec8469863281fYigit Boyar targetPosition = mLayoutManager.findLastVisibleItemPosition(); 306542f1260934df280985294eaef1ec8469863281fYigit Boyar } 307542f1260934df280985294eaef1ec8469863281fYigit Boyar final OrientationHelper helper = mLayoutManager.mOrientationHelper; 308542f1260934df280985294eaef1ec8469863281fYigit Boyar final RecyclerView.ViewHolder vh = mRecyclerView 309542f1260934df280985294eaef1ec8469863281fYigit Boyar .findViewHolderForLayoutPosition(targetPosition); 310542f1260934df280985294eaef1ec8469863281fYigit Boyar 311542f1260934df280985294eaef1ec8469863281fYigit Boyar // scroll enough to offset the child 312542f1260934df280985294eaef1ec8469863281fYigit Boyar int startMargin = helper.getDecoratedStart(vh.itemView) - 313542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 314542f1260934df280985294eaef1ec8469863281fYigit Boyar int endMargin = helper.getEndAfterPadding() - 315542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 316542f1260934df280985294eaef1ec8469863281fYigit Boyar Log.d(TAG, "initial start margin " + startMargin + " , end margin:" + endMargin); 317542f1260934df280985294eaef1ec8469863281fYigit Boyar requestFocus(vh.itemView); 318542f1260934df280985294eaef1ec8469863281fYigit Boyar runTestOnUiThread(new Runnable() { 319542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 320542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 321542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("view should gain the focus", vh.itemView.hasFocus()); 322542f1260934df280985294eaef1ec8469863281fYigit Boyar } 323542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 324542f1260934df280985294eaef1ec8469863281fYigit Boyar do { 325542f1260934df280985294eaef1ec8469863281fYigit Boyar Thread.sleep(100); 326542f1260934df280985294eaef1ec8469863281fYigit Boyar } while (mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE); 327542f1260934df280985294eaef1ec8469863281fYigit Boyar // scroll enough to offset the child 328542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(vh.itemView) - 329542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 330542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - 331542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 332542f1260934df280985294eaef1ec8469863281fYigit Boyar 333542f1260934df280985294eaef1ec8469863281fYigit Boyar Log.d(TAG, "start margin " + startMargin + " , end margin:" + endMargin); 334542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("View should become fully visible", startMargin >= 0 && endMargin >= 0); 335542f1260934df280985294eaef1ec8469863281fYigit Boyar 336542f1260934df280985294eaef1ec8469863281fYigit Boyar int expectedOffset = 0; 337542f1260934df280985294eaef1ec8469863281fYigit Boyar boolean offsetAtStart = false; 338542f1260934df280985294eaef1ec8469863281fYigit Boyar if (!fullyVisible) { 339542f1260934df280985294eaef1ec8469863281fYigit Boyar // move it a bit such that it is no more fully visible 340542f1260934df280985294eaef1ec8469863281fYigit Boyar final int childSize = helper 341542f1260934df280985294eaef1ec8469863281fYigit Boyar .getDecoratedMeasurement(vh.itemView); 342542f1260934df280985294eaef1ec8469863281fYigit Boyar expectedOffset = childSize / 3; 343542f1260934df280985294eaef1ec8469863281fYigit Boyar if (startMargin < endMargin) { 344542f1260934df280985294eaef1ec8469863281fYigit Boyar scrollBy(expectedOffset); 345542f1260934df280985294eaef1ec8469863281fYigit Boyar offsetAtStart = true; 346542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 347542f1260934df280985294eaef1ec8469863281fYigit Boyar scrollBy(-expectedOffset); 348542f1260934df280985294eaef1ec8469863281fYigit Boyar offsetAtStart = false; 349542f1260934df280985294eaef1ec8469863281fYigit Boyar } 350542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(vh.itemView) - 351542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getStartAfterPadding(); 352542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - 353542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedEnd(vh.itemView); 354542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("test sanity, view should not be fully visible", startMargin < 0 355542f1260934df280985294eaef1ec8469863281fYigit Boyar || endMargin < 0); 356542f1260934df280985294eaef1ec8469863281fYigit Boyar } 357542f1260934df280985294eaef1ec8469863281fYigit Boyar 358542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.expectLayouts(1); 359542f1260934df280985294eaef1ec8469863281fYigit Boyar runTestOnUiThread(new Runnable() { 360542f1260934df280985294eaef1ec8469863281fYigit Boyar @Override 361542f1260934df280985294eaef1ec8469863281fYigit Boyar public void run() { 362542f1260934df280985294eaef1ec8469863281fYigit Boyar final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams(); 363542f1260934df280985294eaef1ec8469863281fYigit Boyar if (config.mOrientation == HORIZONTAL) { 364542f1260934df280985294eaef1ec8469863281fYigit Boyar layoutParams.width = mRecyclerView.getWidth() / 2; 365542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 366542f1260934df280985294eaef1ec8469863281fYigit Boyar layoutParams.height = mRecyclerView.getHeight() / 2; 367542f1260934df280985294eaef1ec8469863281fYigit Boyar } 368542f1260934df280985294eaef1ec8469863281fYigit Boyar mRecyclerView.setLayoutParams(layoutParams); 369542f1260934df280985294eaef1ec8469863281fYigit Boyar } 370542f1260934df280985294eaef1ec8469863281fYigit Boyar }); 371542f1260934df280985294eaef1ec8469863281fYigit Boyar Thread.sleep(100); 372542f1260934df280985294eaef1ec8469863281fYigit Boyar // add a bunch of items right before that view, make sure it keeps its position 373542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.waitForLayout(2); 374542f1260934df280985294eaef1ec8469863281fYigit Boyar mLayoutManager.waitForAnimationsToEnd(20); 375542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("view should preserve the focus", vh.itemView.hasFocus()); 376542f1260934df280985294eaef1ec8469863281fYigit Boyar final RecyclerView.ViewHolder postVH = mRecyclerView 377542f1260934df280985294eaef1ec8469863281fYigit Boyar .findViewHolderForLayoutPosition(targetPosition); 378542f1260934df280985294eaef1ec8469863281fYigit Boyar assertNotNull("focused child should stay in layout", postVH); 379542f1260934df280985294eaef1ec8469863281fYigit Boyar assertSame("same view holder should be kept for unchanged child", vh, postVH); 380542f1260934df280985294eaef1ec8469863281fYigit Boyar View focused = postVH.itemView; 381542f1260934df280985294eaef1ec8469863281fYigit Boyar 382542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin = helper.getDecoratedStart(focused) - helper.getStartAfterPadding(); 383542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin = helper.getEndAfterPadding() - helper.getDecoratedEnd(focused); 384542f1260934df280985294eaef1ec8469863281fYigit Boyar 385542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child should be somewhat visible", 386542f1260934df280985294eaef1ec8469863281fYigit Boyar helper.getDecoratedStart(focused) < helper.getEndAfterPadding() 387542f1260934df280985294eaef1ec8469863281fYigit Boyar && helper.getDecoratedEnd(focused) > helper.getStartAfterPadding()); 388542f1260934df280985294eaef1ec8469863281fYigit Boyar if (fullyVisible) { 389542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child end should stay fully visible", 390542f1260934df280985294eaef1ec8469863281fYigit Boyar endMargin >= 0); 391542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("focused child start should stay fully visible", 392542f1260934df280985294eaef1ec8469863281fYigit Boyar startMargin >= 0); 393542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 394542f1260934df280985294eaef1ec8469863281fYigit Boyar if (offsetAtStart) { 395542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("start should preserve its offset", startMargin < 0); 396542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("end should be visible", endMargin >= 0); 397542f1260934df280985294eaef1ec8469863281fYigit Boyar } else { 398542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("end should preserve its offset", endMargin < 0); 399542f1260934df280985294eaef1ec8469863281fYigit Boyar assertTrue("start should be visible", startMargin >= 0); 400542f1260934df280985294eaef1ec8469863281fYigit Boyar } 401542f1260934df280985294eaef1ec8469863281fYigit Boyar } 402542f1260934df280985294eaef1ec8469863281fYigit Boyar } 403542f1260934df280985294eaef1ec8469863281fYigit Boyar 404245b9720dad47a694d16a1d0f48ad462bc27989fYigit Boyar public void testResize() throws Throwable { 405310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5 406310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar , Config.DEFAULT_ITEM_COUNT)) { 407310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar stackFromEndTest(config); 408310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar removeRecyclerView(); 409310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 410310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 411310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 412c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public void testScrollToPositionWithOffset() throws Throwable { 413c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar for (Config config : mBaseVariations) { 414c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffsetTest(config.itemCount(300)); 415c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar removeRecyclerView(); 416c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 417c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 418c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 419c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar public void scrollToPositionWithOffsetTest(Config config) throws Throwable { 420c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar setupByConfig(config, true); 421c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar OrientationHelper orientationHelper = OrientationHelper 422c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .createOrientationHelper(mLayoutManager, config.mOrientation); 423c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Rect layoutBounds = getDecoratedRecyclerViewBounds(); 424c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // try scrolling towards head, should not affect anything 425c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 426c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (config.mStackFromEnd) { 427c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() - 1, 428c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.mOrientationHelper.getEnd() - 500); 429c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 430c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(0, 20); 431c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 432c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertRectSetsEqual(config + " trying to over scroll with offset should be no-op", 433c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar before, mLayoutManager.collectChildCoordinates()); 434c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // try offsetting some visible children 435c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar int testCount = 10; 436c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar while (testCount-- > 0) { 437c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // get middle child 438c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2); 439115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar final int position = mRecyclerView.getChildLayoutPosition(child); 440c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int startOffset = config.mReverseLayout ? 441c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 442c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getDecoratedEnd(child) 443c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 444c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getStartAfterPadding(); 445c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int scrollOffset = config.mStackFromEnd ? startOffset + startOffset / 2 446c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar : startOffset / 2; 447c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.expectLayouts(1); 448c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(position, scrollOffset); 449c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.waitForLayout(2); 450c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int finalOffset = config.mReverseLayout ? 451c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getEndAfterPadding() - orientationHelper 452c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getDecoratedEnd(child) 453c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar : orientationHelper.getDecoratedStart(child) - orientationHelper 454c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar .getStartAfterPadding(); 455c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertEquals(config + " scroll with offset on a visible child should work fine " + 456c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar " offset:" + finalOffset + " , existing offset:" + startOffset + ", " 457c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + "child " + position, 458c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollOffset, finalOffset); 459c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 460c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 461c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // try scrolling to invisible children 462c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar testCount = 10; 463c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // we test above and below, one by one 464c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar int offsetMultiplier = -1; 465c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar while (testCount-- > 0) { 466c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final TargetTuple target = findInvisibleTarget(config); 467c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final String logPrefix = config + " " + target; 468c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.expectLayouts(1); 469c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int offset = offsetMultiplier 470c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3; 471c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar scrollToPositionWithOffset(target.mPosition, offset); 472c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar mLayoutManager.waitForLayout(2); 473c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final View child = mLayoutManager.findViewByPosition(target.mPosition); 474c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertNotNull(logPrefix + " scrolling to a mPosition with offset " + offset 475c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + " should layout it", child); 476c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final Rect bounds = mLayoutManager.getViewBounds(child); 477c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (DEBUG) { 478c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Log.d(TAG, logPrefix + " post scroll to invisible mPosition " + bounds + " in " 479c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + layoutBounds + " with offset " + offset); 480c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 481c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 482c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (config.mReverseLayout) { 483c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertEquals(logPrefix + " when scrolling with offset to an invisible in reverse " 484c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + "layout, its end should align with recycler view's end - offset", 485c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getEndAfterPadding() - offset, 486c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getDecoratedEnd(child) 487c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 488c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 489c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar assertEquals(logPrefix + " when scrolling with offset to an invisible child in normal" 490c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + " layout its start should align with recycler view's start + " 491c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar + "offset", 492c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getStartAfterPadding() + offset, 493c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar orientationHelper.getDecoratedStart(child) 494c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar ); 495c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 496c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar offsetMultiplier *= -1; 497c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 498c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 499c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 500c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar private TargetTuple findInvisibleTarget(Config config) { 501c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE; 502c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar for (int i = 0; i < mLayoutManager.getChildCount(); i++) { 503c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar View child = mLayoutManager.getChildAt(i); 504115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar int position = mRecyclerView.getChildLayoutPosition(child); 505c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (position < minPosition) { 506c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar minPosition = position; 507c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 508c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (position > maxPosition) { 509c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar maxPosition = position; 510c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 511c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 512c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int tailTarget = maxPosition + 513c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar (mRecyclerView.getAdapter().getItemCount() - maxPosition) / 2; 514c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int headTarget = minPosition / 2; 515c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int target; 516c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar // where will the child come from ? 517c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar final int itemLayoutDirection; 518c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) { 519c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar target = tailTarget; 520c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END; 521c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } else { 522c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar target = headTarget; 523c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START; 524c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 525c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar if (DEBUG) { 526c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar Log.d(TAG, 527c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition); 528c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 529c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar return new TargetTuple(target, itemLayoutDirection); 530c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar } 531c50c4cad31d73e574b27bb3d7581542975e37263Yigit Boyar 532310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void stackFromEndTest(final Config config) throws Throwable { 533310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final FrameLayout container = getRecyclerViewContainer(); 534310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 535310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 536310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 537310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, 0, 0); 538310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 539310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 540310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 541310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(config, true); 542310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition(); 543310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition(); 544310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int lastCompletelyVisibleItemPosition = mLayoutManager.findLastCompletelyVisibleItemPosition(); 545310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition(); 546310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.expectLayouts(1); 547310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar // resize the recycler view to half 548310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar runTestOnUiThread(new Runnable() { 549310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 550310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void run() { 551310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mOrientation == HORIZONTAL) { 552310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, container.getWidth() / 2, 0); 553310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 554310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar container.setPadding(0, 0, 0, container.getWidth() / 2); 555310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 556310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 557310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar }); 558310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.waitForLayout(1); 559310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mStackFromEnd) { 560310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last visible position should not change.", 561310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition()); 562310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last completely visible position should not change", 563310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar lastCompletelyVisibleItemPosition, 564310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition()); 565310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } else { 566310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: first visible position should not change.", 567310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition()); 568310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar assertEquals("[" + config + "]: last completely visible position should not change", 569310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar firstCompletelyVisibleItemPosition, 570310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition()); 571310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 572310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 573310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 5746e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void testScrollToPositionWithPredictive() throws Throwable { 5756e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET); 5766e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 5776e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(3, 20); 5786e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 5796e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 5806e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar LinearLayoutManager.INVALID_OFFSET); 5816e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar removeRecyclerView(); 5826e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10); 5836e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 5846e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 5856e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset) 5866e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throws Throwable { 587310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 5886e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 5896e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.mOnLayoutListener = new OnLayoutListener() { 5906e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 5916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state) { 5926e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (state.isPreLayout()) { 5936e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position should still be pending", 5946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollPosition, mLayoutManager.mPendingScrollPosition); 5956e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 5966e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("pending scroll position offset should still be pending", 5976e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset, mLayoutManager.mPendingScrollPositionOffset); 5986e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 5996e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 6006e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar RecyclerView.ViewHolder vh = 601115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar mRecyclerView.findViewHolderForLayoutPosition(scrollPosition); 6026e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertNotNull("scroll to position should work", vh); 6036e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) { 6046e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar assertEquals("scroll offset should be applied properly", 6056e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getPaddingTop() + scrollOffset + 6066e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar ((RecyclerView.LayoutParams) vh.itemView 6076e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar .getLayoutParams()).topMargin, 6086e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.getDecoratedTop(vh.itemView)); 6096e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6106e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6116e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6126e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }; 6136e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.expectLayouts(2); 6146e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar runTestOnUiThread(new Runnable() { 6156e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar @Override 6166e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar public void run() { 6176e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 6186e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mTestAdapter.addAndNotify(0, 1); 6196e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) { 6206e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPosition(scrollPosition); 6216e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } else { 6226e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.scrollToPositionWithOffset(scrollPosition, 6236e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar scrollOffset); 6246e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6256e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 6266e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable throwable) { 6276e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar throwable.printStackTrace(); 6286e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 6306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar }); 6326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mLayoutManager.waitForLayout(2); 6336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar checkForMainThreadException(); 6346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 6356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 6368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar private void waitForFirstLayout() throws Throwable { 637d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 6388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(mRecyclerView); 639d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 640d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 641d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 642504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void testRecycleDuringAnimations() throws Throwable { 643504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final AtomicInteger childCount = new AtomicInteger(0); 644504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestAdapter adapter = new TestAdapter(300) { 645504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 646504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public TestViewHolder onCreateViewHolder(ViewGroup parent, 647504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int viewType) { 648504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 649504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType); 650504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 651504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(create):" + cnt + ", " + testViewHolder); 652504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 653504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return testViewHolder; 654504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 655504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 656310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar setupByConfig(new Config(VERTICAL, false, false).itemCount(300) 657504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar .adapter(adapter), true); 658504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 659504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() { 660504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 661504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void putRecycledView(RecyclerView.ViewHolder scrap) { 662504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.putRecycledView(scrap); 663504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar int cnt = childCount.decrementAndGet(); 664504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 665504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(put):" + cnt + ", " + scrap); 666504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 667504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 668504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 669504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 670504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public RecyclerView.ViewHolder getRecycledView(int viewType) { 671504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType); 672504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (recycledView != null) { 673504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar final int cnt = childCount.incrementAndGet(); 674504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 675504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "CHILD_CNT(get):" + cnt + ", " + recycledView); 676504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 677504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 678504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return recycledView; 679504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 680504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar }; 681504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar pool.setMaxRecycledViews(mTestAdapter.getItemViewType(0), 500); 682504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.setRecycledViewPool(pool); 683504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 684504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 685504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now keep adding children to trigger more children being created etc. 686504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 687504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(15, 1); 688504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(15); 689504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 690504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 691504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 692504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 693504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 694504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 695504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar // now trigger lots of add again, followed by a scroll to position 696504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar for (int i = 0; i < 100; i ++) { 697504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar adapter.addAndNotify(5 + (i % 3) * 3, 1); 698504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Thread.sleep(25); 699504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 700504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar smoothScrollToPosition(mLayoutManager.findLastVisibleItemPosition() + 20); 701504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar waitForAnimations(2); 702504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar getInstrumentation().waitForIdleSync(); 703504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar assertEquals("Children count should add up", childCount.get(), 704504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size()); 705504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 706504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 707d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 708d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void testGetFirstLastChildrenTest() throws Throwable { 709d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (Config config : mBaseVariations) { 710d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar getFirstLastChildrenTest(config); 711d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 712d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 713d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 71449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testDontRecycleChildrenOnDetach() throws Throwable { 71549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(false), true); 71649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 71749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 71849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 71949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 72049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 72149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("No views are recycled", recyclerSize, 72249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 72349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 72449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 72549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 72649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 72749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void testRecycleChildrenOnDetach() throws Throwable { 72849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar setupByConfig(new Config().recycleChildrenOnDetach(true), true); 72949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar final int childCount = mLayoutManager.getChildCount(); 73049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar runTestOnUiThread(new Runnable() { 73149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 73249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar public void run() { 73349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size(); 73449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().setMaxRecycledViews( 73549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mTestAdapter.getItemViewType(0), recyclerSize + childCount); 73649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.setLayoutManager(new TestLayoutManager()); 73749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar assertEquals("All children should be recycled", childCount + recyclerSize, 73849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecyclerView.mRecycler.getRecycledViewPool().size()); 73949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 74049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }); 74149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 74249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 743d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void getFirstLastChildrenTest(final Config config) throws Throwable { 7448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, true); 745d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Runnable viewInBoundsTest = new Runnable() { 746d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 747d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 748d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren(); 749d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final String boundsLog = mLayoutManager.getBoundsLog(); 750d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nfirst visible child should match traversal result\n" 751d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstVisiblePosition, 752d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstVisibleItemPosition() 753d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 754d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 755d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nfirst fully visible child should match traversal result\n" 756d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.firstFullyVisiblePosition, 757d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findFirstCompletelyVisibleItemPosition() 758d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 759d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 760d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals(config + ":\nlast visible child should match traversal result\n" 761d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastVisiblePosition, 762d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastVisibleItemPosition() 763d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 764d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals( 765d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar config + ":\nlast fully visible child should match traversal result\n" 766d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar + boundsLog, visibleChildren.lastFullyVisiblePosition, 767d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 768d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ); 769d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 770d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 771d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 772d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching 773d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // case 774d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount(); 775d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 776d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 777d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 778d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.smoothScrollToPosition(scrollPosition); 779d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 780d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 781d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar while (mLayoutManager.isSmoothScrolling() || 782d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) { 783d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 7848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar Thread.sleep(400); 785d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 786d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // delete all items 787d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(2); 788d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount()); 789d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 790d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // test empty case 791d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 792d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar // set a new adapter with huge items to test full bounds check 793d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.expectLayouts(1); 794d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace(); 795d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final TestAdapter newAdapter = new TestAdapter(100) { 796d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 797d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 798d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int position) { 799d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super.onBindViewHolder(holder, position); 800310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (config.mOrientation == HORIZONTAL) { 801d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumWidth(totalSpace + 5); 802d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } else { 803d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar holder.itemView.setMinimumHeight(totalSpace + 5); 804d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 805d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 806d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }; 807d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(new Runnable() { 808d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 809d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void run() { 810d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mRecyclerView.setAdapter(newAdapter); 811d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 812d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar }); 813d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mLayoutManager.waitForLayout(2); 814d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar runTestOnUiThread(viewInBoundsTest); 815d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 816d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 8178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void testSavedState() throws Throwable { 8188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{ 8198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // do nothing 8238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "doing nothing"; 8288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 8348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPosition(mTestAdapter.getItemCount() * 3 / 4); 8358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 8368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8408edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position"; 8418edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8428edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 8478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3, 8488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 50); 8498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 8508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with positive offset"; 8558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostLayoutRunnable() { 8588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() throws Throwable { 8608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 8618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3, 8623d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev -10); // Some tests break if this value is below the item height. 8638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 8648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "scroll to position with negative offset"; 8698edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8708edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8718edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 8728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8738edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{ 8748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 8758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Doing nothing"; 8788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8798edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8808edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 8818edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8828edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 8838edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // update config as well so that restore assertions will work 8848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation = 1 - config.mOrientation; 8858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setOrientation(config.mOrientation); 8868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 8908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 8918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 8938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 8948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 8958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing orientation"; 8968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 8978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 8988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 8998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 9018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd = !config.mStackFromEnd; 9028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 9038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 9078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; //stack from end should not move items on change 9088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 9128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing stack from end"; 9138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }, 9158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar new PostRestoreRunnable() { 9168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 9188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout = !config.mReverseLayout; 9198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.setReverseLayout(config.mReverseLayout); 9208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 9248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return config.mItemCount == 0; 9258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 9288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public String describe() { 9298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return "Changing reverse layout"; 9308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 93175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }, 93275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar new PostRestoreRunnable() { 93349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 93449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar void onAfterRestore(Config config) throws Throwable { 93549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach; 93649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach); 93749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 93849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 93949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 94049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean shouldLayoutMatch(Config config) { 94149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return true; 94249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 94349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 94449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar @Override 94549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar String describe() { 946b97e8219784e623526bc3c6077a698d608f04fd9Yigit Boyar return "Change should recycle children"; 94749c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 94849c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar }, 94949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar new PostRestoreRunnable() { 95075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar int position; 95175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 95275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterRestore(Config config) throws Throwable { 95375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position = mTestAdapter.getItemCount() / 2; 95475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar mLayoutManager.scrollToPosition(position); 95575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 95675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 95775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 95875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean shouldLayoutMatch(Config config) { 95975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar return mTestAdapter.getItemCount() == 0; 96075b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 96175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 96275b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 96375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar String describe() { 964aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar return "Scroll to position " + position ; 96575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 96675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 96775b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar @Override 96875b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 96975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar if (mTestAdapter.getItemCount() > 0) { 970aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar assertEquals(config + ":scrolled view should be last completely visible", 97175b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar position, 972aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar config.mStackFromEnd ? 973aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar mLayoutManager.findLastCompletelyVisibleItemPosition() 974aeb27f42f502f1687a7047bc34deeff17b874287Yigit Boyar : mLayoutManager.findFirstCompletelyVisibleItemPosition()); 97575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 97675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar } 9778edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9788edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }; 97975b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar boolean[] waitForLayoutOptions = new boolean[]{true, false}; 9803d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev boolean[] loadDataAfterRestoreOptions = new boolean[]{true, false}; 98149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300); 98249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true); 98349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar for (Config config : variations) { 9848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) { 9858edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (boolean waitForLayout : waitForLayoutOptions) { 9868edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) { 9873d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev for (boolean loadDataAfterRestore : loadDataAfterRestoreOptions) { 9883d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev savedStateTest((Config) config.clone(), waitForLayout, 9893d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev loadDataAfterRestore, postLayoutRunnable, postRestoreRunnable); 9903d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev removeRecyclerView(); 9913d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 9928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 9988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 9993d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void savedStateTest(Config config, boolean waitForLayout, boolean loadDataAfterRestore, 10008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation) 10018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throws Throwable { 10028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (DEBUG) { 10038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " + 10048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config + " post layout action " + postLayoutOperation.describe() + 10058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "post restore action " + postRestoreOperation.describe()); 10068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setupByConfig(config, false); 10088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 10098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar waitForFirstLayout(); 10108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postLayoutOperation.run(); 10118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> before = mLayoutManager.collectChildCoordinates(); 10138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcelable savedState = mRecyclerView.onSaveInstanceState(); 10148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // we append a suffix to the parcelable to test out of bounds 10158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar String parcelSuffix = UUID.randomUUID().toString(); 10168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Parcel parcel = Parcel.obtain(); 10178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState.writeToParcel(parcel, 0); 10188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.writeString(parcelSuffix); 10198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 10208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // reset for reading 10218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.setDataPosition(0); 10228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // re-create 10238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 10248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar removeRecyclerView(); 10258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 10263d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev final int itemCount = mTestAdapter.getItemCount(); 10273d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev if (loadDataAfterRestore) { 10283d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mTestAdapter.deleteAndNotify(0, itemCount); 10293d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 10303d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView restored = new RecyclerView(getActivity()); 10328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // this config should be no op. 10338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager = new WrappedLinearLayoutManager(getActivity(), 1034b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar config.mOrientation, config.mReverseLayout); 1035b8403301bbec29129730f6cce3fe2fa3ee8e1e0bYigit Boyar mLayoutManager.setStackFromEnd(config.mStackFromEnd); 10368edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setLayoutManager(mLayoutManager); 10378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // use the same adapter for Rect matching 10388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.setAdapter(mTestAdapter); 10398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar restored.onRestoreInstanceState(savedState); 10403d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10413d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev if (loadDataAfterRestore) { 10423d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mTestAdapter.addAndNotify(itemCount); 10433d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 10443d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10458edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar postRestoreOperation.onAfterRestore(config); 10468edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 10478edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar parcel.readString()); 10488edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.expectLayouts(1); 10498edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar setRecyclerView(restored); 10508edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.waitForLayout(2); 10518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // calculate prefix here instead of above to include post restore changes 10528edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() + 10538edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar "\npostRestore:" + postRestoreOperation.describe() + "\n"; 10548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, reverse layout should be preserved", 10558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mReverseLayout, mLayoutManager.getReverseLayout()); 10568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, orientation should be preserved", 10578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mOrientation, mLayoutManager.getOrientation()); 10588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(logPrefix + " on saved state, stack from end should be preserved", 10598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar config.mStackFromEnd, mLayoutManager.getStackFromEnd()); 10608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (waitForLayout) { 10613d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev final boolean strictItemEquality = !loadDataAfterRestore; 10628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (postRestoreOperation.shouldLayoutMatch(config)) { 10638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsEqual( 10648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix + ": on restore, previous view positions should be preserved", 10653d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev before, mLayoutManager.collectChildCoordinates(), strictItemEquality); 10668edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 10678edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertRectSetsNotEqual( 10688edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar logPrefix 106949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + ": on restore with changes, previous view positions should NOT " 107049c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar + "be preserved", 10713d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev before, mLayoutManager.collectChildCoordinates(), strictItemEquality); 10728edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 107375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar postRestoreOperation.onAfterReLayout(config); 10748edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10758edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 10768edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 10773d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void testScrollAndClear() throws Throwable { 10783d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev setupByConfig(new Config(), true); 10793d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10803d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertTrue("Children not laid out", mLayoutManager.collectChildCoordinates().size() > 0); 10813d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10823d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mLayoutManager.expectLayouts(1); 10833d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev runTestOnUiThread(new Runnable() { 10843d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev @Override 10853d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void run() { 10863d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mLayoutManager.scrollToPositionWithOffset(1, 0); 10873d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mTestAdapter.clearOnUIThread(); 10883d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 10893d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev }); 10903d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev mLayoutManager.waitForLayout(2); 10913d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10923d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertEquals("Remaining children", 0, mLayoutManager.collectChildCoordinates().size()); 10933d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 10943d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10953d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 10968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 10978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 10988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 10998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 11008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mLayoutManager.scrollToPositionWithOffset(position, offset); 11018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 11038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsNotEqual(String message, Map<Item, Rect> before, 11063d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev Map<Item, Rect> after, boolean strictItemEquality) { 11078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Throwable throwable = null; 11088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar try { 11093d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertRectSetsEqual("NOT " + message, before, after, strictItemEquality); 11108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } catch (Throwable t) { 11118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar throwable = t; 11128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertNotNull(message + "\ntwo layout should be different", throwable); 11148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 11168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) { 11173d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertRectSetsEqual(message, before, after, true); 11183d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 11193d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev 11203d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after, 11213d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev boolean strictItemEquality) { 1122668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar StringBuilder sb = new StringBuilder(); 11233d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev sb.append("checking rectangle equality.\n"); 11243d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev sb.append("before:\n"); 1125668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 1126668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 1127668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar } 1128668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append("after:\n"); 1129668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar for (Map.Entry<Item, Rect> entry : after.entrySet()) { 1130668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n"); 11318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 1132668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar message = message + "\n" + sb.toString(); 11338edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nitem counts should be equal", before.size() 11348edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar , after.size()); 11358edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (Map.Entry<Item, Rect> entry : before.entrySet()) { 11363d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev final Item beforeItem = entry.getKey(); 11373d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev Rect afterRect = null; 11383d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev if (strictItemEquality) { 11393d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev afterRect = after.get(beforeItem); 11403d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertNotNull(message + ":\nSame item should be visible after simple re-layout", 11413d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev afterRect); 11423d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } else { 11433d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) { 11443d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev final Item afterItem = afterEntry.getKey(); 11453d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) { 11463d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev afterRect = afterEntry.getValue(); 11473d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev break; 11483d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 11493d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 11503d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev assertNotNull(message + ":\nItem with same adapter index should be visible " + 11513d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev "after simple re-layout", 11523d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev afterRect); 11533d8453880afb3e32c4c59c52b8b580f91d78b29fVladislav Kaznacheev } 11548edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar assertEquals(message + ":\nItem should be laid out at the same coordinates", 11558edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar entry.getValue(), afterRect); 11568edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11578edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 11588edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1159a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void testAccessibilityPositions() throws Throwable { 1160a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar setupByConfig(new Config(VERTICAL, false, false), true); 1161a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityDelegateCompat delegateCompat = mRecyclerView 1162a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar .getCompatAccessibilityDelegate(); 1163a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityEvent event = AccessibilityEvent.obtain(); 1164a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar runTestOnUiThread(new Runnable() { 1165a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar @Override 1166a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar public void run() { 1167a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event); 1168a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1169a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar }); 1170a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar final AccessibilityRecordCompat record = AccessibilityEventCompat 1171a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar .asRecord(event); 1172a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar assertEquals("result should have first position", 1173a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar record.getFromIndex(), 1174a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mLayoutManager.findFirstVisibleItemPosition()); 1175a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar assertEquals("result should have last position", 1176a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar record.getToIndex(), 1177a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar mLayoutManager.findLastVisibleItemPosition()); 1178a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar } 1179a910619e83d0052e1d81aa5fe532821a2f99d76cYigit Boyar 1180e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void testPrepareForDrop() throws Throwable { 1181e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar SelectTargetChildren[] selectors = new SelectTargetChildren[] { 1182e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new SelectTargetChildren() { 1183e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1184e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int[] selectTargetChildren(int childCount) { 1185e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return new int[]{1, 0}; 1186e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1187e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }, 1188e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new SelectTargetChildren() { 1189e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1190e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int[] selectTargetChildren(int childCount) { 1191e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return new int[]{0, 1}; 1192e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1193e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }, 1194e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new SelectTargetChildren() { 1195e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1196e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int[] selectTargetChildren(int childCount) { 1197e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return new int[]{childCount - 1, childCount - 2}; 1198e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1199e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }, 1200e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new SelectTargetChildren() { 1201e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1202e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int[] selectTargetChildren(int childCount) { 1203e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return new int[]{childCount - 2, childCount - 1}; 1204e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1205e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }, 1206e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new SelectTargetChildren() { 1207e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1208e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int[] selectTargetChildren(int childCount) { 1209e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return new int[]{childCount / 2, childCount / 2 + 1}; 1210e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1211e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }, 1212e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new SelectTargetChildren() { 1213e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1214e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int[] selectTargetChildren(int childCount) { 1215e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return new int[]{childCount / 2 + 1, childCount / 2}; 1216e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1217e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1218e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 1219e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (SelectTargetChildren selector : selectors) { 1220e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (Config config : mBaseVariations) { 1221e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar prepareForDropTest(config, selector); 1222e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar removeRecyclerView(); 1223e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1224e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1225e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1226e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1227e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void prepareForDropTest(final Config config, SelectTargetChildren selectTargetChildren) 1228e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar throws Throwable { 1229e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar config.mTestAdapter = new TestAdapter(100) { 1230e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1231e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onBindViewHolder(TestViewHolder holder, 1232e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int position) { 1233e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar super.onBindViewHolder(holder, position); 1234e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1235e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int base = mRecyclerView.getWidth() / 5; 1236e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int itemRand = holder.mBoundItem.mText.hashCode() % base; 1237e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar holder.itemView.setMinimumWidth(base + itemRand); 1238e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1239e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int base = mRecyclerView.getHeight() / 5; 1240e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int itemRand = holder.mBoundItem.mText.hashCode() % base; 1241e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar holder.itemView.setMinimumHeight(base + itemRand); 1242e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1243e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1244e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 1245e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar setupByConfig(config, true); 1246e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mLayoutManager.expectLayouts(1); 1247e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollToPosition(mTestAdapter.getItemCount() / 2); 1248e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mLayoutManager.waitForLayout(1); 1249e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int[] positions = selectTargetChildren.selectTargetChildren(mRecyclerView.getChildCount()); 1250e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final View fromChild = mLayoutManager.getChildAt(positions[0]); 1251e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int fromPos = mLayoutManager.getPosition(fromChild); 1252e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final View onChild = mLayoutManager.getChildAt(positions[1]); 1253e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int toPos = mLayoutManager.getPosition(onChild); 1254e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final OrientationHelper helper = mLayoutManager.mOrientationHelper; 1255e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dragCoordinate; 1256e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final boolean towardsHead = toPos < fromPos; 1257e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int referenceLine; 1258e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (config.mReverseLayout == towardsHead) { 1259e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar referenceLine = helper.getDecoratedEnd(onChild); 1260e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar dragCoordinate = referenceLine + 3 - 1261e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar helper.getDecoratedMeasurement(fromChild); 1262e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1263e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar referenceLine = helper.getDecoratedStart(onChild); 1264e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar dragCoordinate = referenceLine - 3; 1265e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1266e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mLayoutManager.expectLayouts(2); 1267e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1268e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int x,y; 1269e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (config.mOrientation == HORIZONTAL) { 1270e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar x = dragCoordinate; 1271e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar y = fromChild.getTop(); 1272e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1273e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar y = dragCoordinate; 1274e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar x = fromChild.getLeft(); 1275e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1276e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar runTestOnUiThread(new Runnable() { 1277e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1278e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void run() { 1279e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mTestAdapter.moveInUIThread(fromPos, toPos); 1280e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mTestAdapter.notifyItemMoved(fromPos, toPos); 1281e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mLayoutManager.prepareForDrop(fromChild, onChild, x, y); 1282e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1283e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }); 1284e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mLayoutManager.waitForLayout(2); 1285e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1286e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar assertSame(fromChild, mRecyclerView.findViewHolderForAdapterPosition(toPos).itemView); 1287e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // make sure it has the position we wanted 1288e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (config.mReverseLayout == towardsHead) { 1289e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar assertEquals(referenceLine, helper.getDecoratedEnd(fromChild)); 1290e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1291e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar assertEquals(referenceLine, helper.getDecoratedStart(fromChild)); 1292e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1293e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1294e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1295d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar static class VisibleChildren { 1296d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1297d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstVisiblePosition = RecyclerView.NO_POSITION; 1298d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1299d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int firstFullyVisiblePosition = RecyclerView.NO_POSITION; 1300d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1301d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastVisiblePosition = RecyclerView.NO_POSITION; 1302d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1303d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int lastFullyVisiblePosition = RecyclerView.NO_POSITION; 1304d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1305d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 1306d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 1307d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "VisibleChildren{" + 1308d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "firstVisiblePosition=" + firstVisiblePosition + 1309d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", firstFullyVisiblePosition=" + firstFullyVisiblePosition + 1310d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastVisiblePosition=" + lastVisiblePosition + 1311d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", lastFullyVisiblePosition=" + lastFullyVisiblePosition + 1312d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 1313d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1314d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1315d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 13168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostLayoutRunnable { 13178edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13188edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract void run() throws Throwable; 13198edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13208edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 13218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract private class PostRestoreRunnable { 13248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar void onAfterRestore(Config config) throws Throwable { 13268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13278edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13288edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar abstract String describe(); 13298edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13308edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar boolean shouldLayoutMatch(Config config) { 13318edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return true; 13328edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 133375b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 133475b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar void onAfterReLayout(Config config) { 133575b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar 133675b7ff9ccca9311854e9c74282b1af1ce87df470Yigit Boyar }; 13378edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13388edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13398edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar class WrappedLinearLayoutManager extends LinearLayoutManager { 1340d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1341d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar CountDownLatch layoutLatch; 1342d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 13438edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar OrientationHelper mSecondaryOrientation; 13448edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar OnLayoutListener mOnLayoutListener; 13466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 1347d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 1348d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar super(context, orientation, reverseLayout); 1349d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1350d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1351d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void expectLayouts(int count) { 1352d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch = new CountDownLatch(count); 1353d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1354d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1355d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void waitForLayout(long timeout) throws InterruptedException { 1356d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar waitForLayout(timeout, TimeUnit.SECONDS); 1357d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1358d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 13598edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 13608edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void setOrientation(int orientation) { 13618edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar super.setOrientation(orientation); 13628edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation = null; 13638edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13648edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 13658edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 1366504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) { 1367504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 1368504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child)); 1369504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1370504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleView(child, recycler); 1371504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1372504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 1373504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 1374504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { 1375504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar if (DEBUG) { 1376504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index))); 1377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1378504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar super.removeAndRecycleViewAt(index, recycler); 1379504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1380504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 1381504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar @Override 138294c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar void ensureLayoutState() { 138394c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar super.ensureLayoutState(); 13848edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (mSecondaryOrientation == null) { 138594c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar mSecondaryOrientation = OrientationHelper.createOrientationHelper(this, 138694c0b086c12e634976fecd47d442bc7a1a6341bbYigit Boyar 1 - getOrientation()); 13878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 13898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1390d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException { 13916e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit); 1392d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar assertEquals("all expected layouts should be executed at the expected time", 1393d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 0, layoutLatch.getCount()); 13946e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar getInstrumentation().waitForIdleSync(); 1395d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1396d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1397888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar @Override 1398888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar LayoutState createLayoutState() { 1399888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return new LayoutState() { 1400888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar @Override 1401888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar View next(RecyclerView.Recycler recycler) { 1402888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar final boolean hadMore = hasMore(mRecyclerView.mState); 1403888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar final int position = mCurrentPosition; 1404888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar View next = super.next(recycler); 1405888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar assertEquals("if has more, should return a view", hadMore, next != null); 1406888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar assertEquals("position of the returned view must match current position", 1407888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition()); 1408888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar return next; 1409888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 1410888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar }; 1411888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar } 1412888093b0a071a99d65c01116cf703d46b21f9918Yigit Boyar 1413d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String getBoundsLog() { 1414d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar StringBuilder sb = new StringBuilder(); 1415d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding()) 1416d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding()); 1417d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("\nchildren bounds\n"); 1418d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childCount = getChildCount(); 1419d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 1420d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 1421d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child)) 1422d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append("[").append("start:").append( 1423d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientationHelper.getDecoratedStart(child)).append(", end:") 1424d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n"); 1425d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1426d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return sb.toString(); 1427d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1428d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1429310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException { 1430310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator(); 1431310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (itemAnimator == null) { 1432310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar return; 1433310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1434310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final CountDownLatch latch = new CountDownLatch(1); 1435310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar final boolean running = itemAnimator.isRunning( 1436310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { 1437310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar @Override 1438310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar public void onAnimationsFinished() { 1439310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar latch.countDown(); 1440310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1441310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1442310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar ); 1443310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar if (running) { 1444310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar latch.await(timeoutInSeconds, TimeUnit.SECONDS); 1445310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1446310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar } 1447310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar 1448d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public VisibleChildren traverseAndFindVisibleChildren() { 1449d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int childCount = getChildCount(); 1450d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final VisibleChildren visibleChildren = new VisibleChildren(); 1451d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int start = mOrientationHelper.getStartAfterPadding(); 1452d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int end = mOrientationHelper.getEndAfterPadding(); 1453d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar for (int i = 0; i < childCount; i++) { 1454d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar View child = getChildAt(i); 1455d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childStart = mOrientationHelper.getDecoratedStart(child); 1456d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int childEnd = mOrientationHelper.getDecoratedEnd(child); 1457d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean fullyVisible = childStart >= start && childEnd <= end; 1458d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final boolean hidden = childEnd <= start || childStart >= end; 1459d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (hidden) { 1460d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar continue; 1461d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1462d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar final int position = getPosition(child); 1463d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (fullyVisible) { 1464d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstFullyVisiblePosition || 1465d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) { 1466d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstFullyVisiblePosition = position; 1467d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1468d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1469d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastFullyVisiblePosition) { 1470d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastFullyVisiblePosition = position; 1471d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1472d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1473d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1474d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position < visibleChildren.firstVisiblePosition || 1475d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) { 1476d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.firstVisiblePosition = position; 1477d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1478d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1479d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar if (position > visibleChildren.lastVisiblePosition) { 1480d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar visibleChildren.lastVisiblePosition = position; 1481d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1482d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1483d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1484d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return visibleChildren; 1485d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1486d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 14878edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Rect getViewBounds(View view) { 14888edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar if (getOrientation() == HORIZONTAL) { 14898edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 14908edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 14918edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 14928edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view), 14938edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view)); 14948edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } else { 14958edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return new Rect( 14968edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedStart(view), 14978edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedStart(view), 14988edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mSecondaryOrientation.getDecoratedEnd(view), 14998edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar mOrientationHelper.getDecoratedEnd(view)); 15008edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 15018edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 15028edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 15038edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 15048edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar Map<Item, Rect> collectChildCoordinates() throws Throwable { 15058edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>(); 15068edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar runTestOnUiThread(new Runnable() { 15078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 15088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public void run() { 15098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar final int childCount = getChildCount(); 1510cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar Rect layoutBounds = new Rect(0, 0, 1511cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar mLayoutManager.getWidth(), mLayoutManager.getHeight()); 15128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar for (int i = 0; i < childCount; i++) { 15138edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar View child = getChildAt(i); 15148edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child 15158edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar .getLayoutParams(); 15168edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar TestViewHolder vh = (TestViewHolder) lp.mViewHolder; 1517cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar Rect childBounds = getViewBounds(child); 1518cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar if (new Rect(childBounds).intersect(layoutBounds)) { 1519cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar items.put(vh.mBoundItem, childBounds); 1520cc1bf428e5bdbcc94e58107da4a100b279810a64Yigit Boyar } 15218edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 15228edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 15238edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar }); 15248edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return items; 15258edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 15268edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1527d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 1528d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 15296e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar try { 15306e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 15316e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.before(recycler, state); 15326e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 15336e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar super.onLayoutChildren(recycler, state); 15346e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar if (mOnLayoutListener != null) { 15356e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar mOnLayoutListener.after(recycler, state); 15366e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 15376e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } catch (Throwable t) { 15386e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar postExceptionToInstrumentation(t); 15396e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 1540d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar layoutLatch.countDown(); 1541d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 15426e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 15436e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 15446e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar } 15456e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar 15466e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar static class OnLayoutListener { 15476e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void before(RecyclerView.Recycler recycler, RecyclerView.State state){} 15486e83751247c5be0211d7bffaf057431c03dfef38Yigit Boyar void after(RecyclerView.Recycler recycler, RecyclerView.State state){} 1549d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1550d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 15518edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar static class Config implements Cloneable { 1552d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 15538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar private static final int DEFAULT_ITEM_COUNT = 100; 1554d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1555d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar private boolean mStackFromEnd; 1556d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1557310e95e1c6dfe4f26ef594233e65e1ff83e0f1ffYigit Boyar int mOrientation = VERTICAL; 1558d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1559d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar boolean mReverseLayout = false; 1560d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 156149c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar boolean mRecycleChildrenOnDetach = false; 156249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 1563d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar int mItemCount = DEFAULT_ITEM_COUNT; 1564d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1565504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar TestAdapter mTestAdapter; 1566504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 1567d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config(int orientation, boolean reverseLayout, boolean stackFromEnd) { 1568d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 1569d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 1570d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromEnd; 1571d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1572d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1573d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config() { 1574d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1575d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1576d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1577504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar Config adapter(TestAdapter adapter) { 1578504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar mTestAdapter = adapter; 1579504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar return this; 1580504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar } 1581504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar 158249c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 158349c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar mRecycleChildrenOnDetach = recycleChildrenOnDetach; 158449c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar return this; 158549c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar } 158649c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar 1587d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config orientation(int orientation) { 1588d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mOrientation = orientation; 1589d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1590d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1591d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1592d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config stackFromBottom(boolean stackFromBottom) { 1593d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mStackFromEnd = stackFromBottom; 1594d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1595d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1596d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1597d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar Config reverseLayout(boolean reverseLayout) { 1598d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mReverseLayout = reverseLayout; 1599d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1600d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1601d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 1602d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public Config itemCount(int itemCount) { 1603d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar mItemCount = itemCount; 1604d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return this; 1605d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1606d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar 16078edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar // required by convention 16088edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar @Override 16098edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar public Object clone() throws CloneNotSupportedException { 16108edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar return super.clone(); 16118edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar } 16128edcb0bdeaba6931f9d8154f0c81f57da7ddab2aYigit Boyar 1613d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar @Override 1614d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar public String toString() { 1615d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar return "Config{" + 1616d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar "mStackFromEnd=" + mStackFromEnd + 1617d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mOrientation=" + mOrientation + 1618d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mReverseLayout=" + mReverseLayout + 161949c83b12201dde5b93d4eca3d44478e0c967a2e6Yigit Boyar ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach + 1620d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar ", mItemCount=" + mItemCount + 1621d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar '}'; 1622d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1623d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar } 1624542f1260934df280985294eaef1ec8469863281fYigit Boyar 1625e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private interface SelectTargetChildren { 1626e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int[] selectTargetChildren(int childCount); 1627542f1260934df280985294eaef1ec8469863281fYigit Boyar } 1628d7848507d6c561ca8e17d1954653f4fd26b58f84Yigit Boyar} 1629